Skip to content

Instantly share code, notes, and snippets.

@michaliskambi
Created November 2, 2023 16:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save michaliskambi/229e111f17269d8dccc1cb76a0d9aafa to your computer and use it in GitHub Desktop.
Save michaliskambi/229e111f17269d8dccc1cb76a0d9aafa to your computer and use it in GitHub Desktop.
Using TExtrusionNode to generate some pipes
{ Main view, where most of the application logic takes place.
Feel free to use this code as a starting point for your own projects.
This template code is in public domain, unlike most other CGE code which
is covered by BSD or LGPL (see https://castle-engine.io/license). }
unit GameViewMain;
interface
uses Classes,
CastleVectors, CastleComponentSerialize,
CastleUIControls, CastleControls, CastleKeysMouse, CastleViewport;
type
{ Main view, where most of the application logic takes place. }
TViewMain = class(TCastleView)
published
{ Components designed using CGE editor.
These fields will be automatically initialized at Start. }
LabelFps: TCastleLabel;
Viewport1: TCastleViewport;
public
constructor Create(AOwner: TComponent); override;
procedure Start; override;
procedure Update(const SecondsPassed: Single; var HandleInput: Boolean); override;
function Press(const Event: TInputPressRelease): Boolean; override;
end;
var
ViewMain: TViewMain;
implementation
uses SysUtils, Math,
X3DNodes, CastleUtils, CastleScene;
{ TViewMain ----------------------------------------------------------------- }
constructor TViewMain.Create(AOwner: TComponent);
begin
inherited;
DesignUrl := 'castle-data:/gameviewmain.castle-user-interface';
end;
procedure TViewMain.Start;
{ Set the cross-section of Extrusion by calling Extrusion.SetCrossSection
to have a circular shape with given Radius. }
procedure SetCrossSectionCircle(const Extrusion: TExtrusionNode; const Radius: Single);
const
CircleSlices = 16;
var
// We create CircleSlices+1 points, the first and last points are equal to close the circle.
Points: array [0..CircleSlices] of TVector2;
I: Integer;
S, C: Single;
begin
for I := Low(Points) to High(Points) do
begin
// Note: we use "2 * Pi - " to invert the order,
// to make CCW (counter-clockwise) order.
SinCos(2 * Pi - 2 * Pi * I / CircleSlices, S, C);
Points[I] := Vector2(C, S) * Radius;
end;
Extrusion.SetCrossSection(Points);
end;
{ Add to Viewport1 a scene with extrusion along the given sequence of points
with cross-section being a circle with given Radius.
The scene is positioned and colored randomly, just for demo. }
procedure AddPipe(const Points: array of TVector3; const Radius: Single);
var
Scene: TCastleScene;
RootNode: TX3DRootNode;
Extrusion: TExtrusionNode;
Shape: TShapeNode;
Appearance: TAppearanceNode;
Material: TMaterialNode;
begin
Extrusion := TExtrusionNode.Create;
Extrusion.SetSpine(Points);
Extrusion.BeginCap := true;
Extrusion.EndCap := true;
Extrusion.CreaseAngle := 4; // Larger than Pi, so perfectly smooth
SetCrossSectionCircle(Extrusion, Radius);
Material := TMaterialNode.Create;
// randomize color
Material.DiffuseColor := Vector3(Random, Random, Random);
Appearance := TAppearanceNode.Create;
Appearance.Material := Material;
Shape := TShapeNode.Create;
Shape.Geometry := Extrusion;
Shape.Appearance := Appearance;
RootNode := TX3DRootNode.Create;
RootNode.AddChildren(Shape);
Scene := TCastleScene.Create(FreeAtStop);
Scene.Load(RootNode, true);
// randomize position
Scene.Translation := Vector3(
RandomFloatRange(-10, 10),
0,
RandomFloatRange(-10, 10)
);
Viewport1.Items.Add(Scene);
end;
var
I: Integer;
begin
inherited;
for I := 1 to 10 do
AddPipe([
Vector3(0, 0, 0),
Vector3(0, 2, 0),
// these 3 points are close to eeach other, to allow smooth change in spine
Vector3(0, 4.9, 0),
Vector3(0, 5, 0),
Vector3(0.1, 5, 0),
Vector3(2, 5, 0),
Vector3(5, 5, 0)
], 0.1);
end;
procedure TViewMain.Update(const SecondsPassed: Single; var HandleInput: Boolean);
begin
inherited;
{ This virtual method is executed every frame (many times per second). }
Assert(LabelFps <> nil, 'If you remove LabelFps from the design, remember to remove also the assignment "LabelFps.Caption := ..." from code');
LabelFps.Caption := 'FPS: ' + Container.Fps.ToString;
end;
function TViewMain.Press(const Event: TInputPressRelease): Boolean;
begin
Result := inherited;
if Result then Exit; // allow the ancestor to handle keys
{ This virtual method is executed when user presses
a key, a mouse button, or touches a touch-screen.
Note that each UI control has also events like OnPress and OnClick.
These events can be used to handle the "press", if it should do something
specific when used in that UI control.
The TViewMain.Press method should be used to handle keys
not handled in children controls.
}
// Use this to handle keys:
{
if Event.IsKey(keyXxx) then
begin
// DoSomething;
Exit(true); // key was handled
end;
}
end;
end.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment