This file is indexed.

/usr/src/castle-game-engine-6.4/x3d/x3dloadinternalgeo.pas is in castle-game-engine-src 6.4+dfsg1-2.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
{
  Copyright 2002-2017 Michalis Kamburelis.

  This file is part of "Castle Game Engine".

  "Castle Game Engine" is free software; see the file COPYING.txt,
  included in this distribution, for details about the copyright.

  "Castle Game Engine" is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

  ----------------------------------------------------------------------------
}

{ Load 3D Videoscape GEO models.
  See [http://local.wasp.uwa.edu.au/~pbourke/dataformats/geo/].
  We handle basic geometry, we can open files exported by Blender exporter. }
unit X3DLoadInternalGEO;

{$I castleconf.inc}

interface

uses X3DNodes;

function LoadGEO(const URL: string): TX3DRootNode;

implementation

uses CastleVectors, CastleUtils, Classes, SysUtils, CastleLog,
  CastleClassUtils, CastleDownload, CastleURIUtils,
  CastleFilesUtils, CastleStringUtils, X3DLoadInternalUtils;

{ TObject3DGEO ---------------------------------------------------------------- }

type
  { Reader of GEO files.
    Note that contents of Verts and Faces are read-only for user of this unit. }
  TObject3DGEO = class
  public
    Verts: TVector3List;
    Faces: TVector3CardinalList;
    constructor Create(const URL: string);
    destructor Destroy; override;
  end;

constructor TObject3DGEO.Create(const URL: string);
type
  TGEOFormatFlavor = (gfOld, gfMeshColorFaces, gfMeshColorVerts);
var
  Flavor: TGEOFormatFlavor;

  function ReadVertexIndex(const S: string): Cardinal;
  begin
    Result := StrToInt(S);
    { In older format, vertex index is 1-based. }
    if Flavor = gfOld then Dec(Result);
  end;

  { Read exactly one line of GEO file, reading new face information.
    Updates Faces. }
  procedure ReadGEOFace(const Line: string);
  var
    J, ThisPolyCount: Integer;
    FirstVert, LastVert: Cardinal;
    CurrentFace: TVector3Cardinal;
    LineTokens: TCastleStringList;
  begin
    LineTokens := CreateTokens(Line);
    try
      if LineTokens.Count = 0 then
      begin
        WritelnWarning('GEO', 'Empty line');
        Exit;
      end;

      ThisPolyCount := StrToInt(LineTokens[0]);
      if ThisPolyCount < 3 then
      begin
        WritelnWarning('GEO', 'Polygon with less than 3 vertexes');
        Exit;
      end;

      if LineTokens.Count < ThisPolyCount + 1 then
      begin
        WritelnWarning('GEO', 'Not enough vertex indexes on line');
        Exit;
      end;

      { the polygon is always at least a triangle, read it }
      for j := 0 to 2 do
        CurrentFace[j] := ReadVertexIndex(LineTokens[J + 1]);
      Faces.Add(CurrentFace);

      FirstVert := CurrentFace[0];
      LastVert := CurrentFace[2];

      { for each following vertex, add new triangle by connecting
        FirstVert, LastVert and new vertexa.
        Watch out for the order --- make all triangles oriented consistently. }
      for j := 3 to ThisPolyCount - 1 do
      begin
        CurrentFace[0] := FirstVert;
        CurrentFace[1] := LastVert;
        CurrentFace[2] := ReadVertexIndex(LineTokens[J + 1]);
        Faces.Add(CurrentFace);

        LastVert := CurrentFace[2];
      end;
    finally FreeAndNil(LineTokens) end;
  end;

var
  Reader: TTextReader;
  i: Integer;
  Line: string;
  VertsCount, PolysCount, VertsInPolysCount: Integer;
begin
 inherited Create;
 Verts := TVector3List.Create;
 Faces := TVector3CardinalList.Create;

 Reader := TTextReader.Create(URL);
 try
  { Read first line: magic number (or not existent in older GEO format) }
  Line := Reader.Readln;
  Line := Trim(Line);
  if SameText(Line, '3DG1') then
    Flavor := gfMeshColorFaces else
  if SameText(Line, 'GOUR') then
    Flavor := gfMeshColorVerts else
    Flavor := gfOld;

  if Flavor = gfOld then
  begin
    { Use current value of Line, for older format the first line contains
      these counts. }
    DeFormat(Line, '%d %d %d', [@VertsCount, @PolysCount, @VertsInPolysCount]);

    { Ile mamy Faces trojkatnych ? Mamy liczbe
      wielokatow = PolysCount. Mamy sumaryczna liczbe wierzcholkow
      w nich. Na kazdy polygon przypadaja co najmniej 3 wierzcholki
      i one daja jeden trojkat. Kazdy nadmiarowy wierzcholek,
      bez wzgledu na to w ktorym polygonie sie znajdzie, spowoduje
      utworzenie nowego trojkata. Stad
        FFacesCount := PolysCount + (VertsInPolysCount - PolysCount * 3);
      czyli
        Faces.SetLength(VertsInPolysCount - PolysCount * 2);

      To cooperate with other Flavor, we do not set Faces.Count directly,
      instead we set only Capacity.
    }
    Faces.Capacity := VertsInPolysCount - PolysCount * 2;
  end else
  begin
    { In newer formats, 2nd line contains just VertsCount. }
    VertsCount := StrToInt(Reader.Readln);
    PolysCount := -1;
  end;

  Verts.Count := VertsCount;
  for i := 0 to Verts.Count-1 do
    Verts.List^[I] := Vector3FromStr(Reader.Readln);

  if PolysCount <> -1 then
  begin
    for i := 0 to PolysCount - 1 do
      ReadGEOFace(Reader.Readln);
  end else
  begin
    { PolysCount not known. So we just read the file as fast as we can. }
    while not Reader.Eof do
      ReadGEOFace(Reader.Readln);
  end;
 finally FreeAndNil(Reader) end;
end;

destructor TObject3DGEO.Destroy;
begin
 Verts.Free;
 Faces.Free;
 inherited;
end;

{ LoadGEO -------------------------------------------------------------------- }

function LoadGEO(const URL: string): TX3DRootNode;
var
  geo: TObject3DGEO;
  verts: TCoordinateNode;
  faces: TIndexedFaceSetNode;
  Shape: TShapeNode;
  i: integer;
  BaseUrl: string;
begin
  BaseUrl := AbsoluteURI(URL);
  geo := TObject3DGEO.Create(URL);
  try
    result := TX3DRootNode.Create('', BaseUrl);
    try
      Result.HasForceVersion := true;
      Result.ForceVersion := X3DVersion;

      Shape := TShapeNode.Create('', BaseUrl);
      result.AddChildren(Shape);
      Shape.Material := TMaterialNode.Create('', BaseUrl);

      faces := TIndexedFaceSetNode.Create('', BaseUrl);
      Shape.FdGeometry.Value := faces;
      faces.FdCreaseAngle.Value := NiceCreaseAngle;
      faces.FdSolid.Value := false;
      faces.FdCoordIndex.Count := geo.Faces.Count * 4;
      for i := 0 to geo.Faces.Count-1 do
      begin
        faces.FdCoordIndex.Items.List^[i * 4    ] := geo.Faces.List^[i][0];
        faces.FdCoordIndex.Items.List^[i * 4 + 1] := geo.Faces.List^[i][1];
        faces.FdCoordIndex.Items.List^[i * 4 + 2] := geo.Faces.List^[i][2];
        faces.FdCoordIndex.Items.List^[i * 4 + 3] := -1;
      end;

      verts := TCoordinateNode.Create('', BaseUrl);
      faces.Coord := verts;
      verts.SetPoint(geo.Verts);
    except result.Free; raise end;
  finally geo.Free end;
end;

end.