GXS.FileVRML.pas 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614
  1. //
  2. // The graphics engine GXScene https://github.com/glscene
  3. //
  4. unit GXS.FileVRML;
  5. (* Preliminary VRML vector file support *)
  6. interface
  7. uses
  8. System.Classes,
  9. System.SysUtils,
  10. System.Math,
  11. Stage.VectorTypes,
  12. Stage.VectorGeometry,
  13. GXS.VectorLists,
  14. GXS.ApplicationFileIO,
  15. GXS.VectorFileObjects,
  16. GXS.Material,
  17. GXS.MeshUtils,
  18. Formatx.VRML;
  19. type
  20. TgxVRMLVectorFile = class(TgxVectorFile)
  21. public
  22. class function Capabilities: TDataFileCapabilities; override;
  23. procedure LoadFromStream(aStream: TStream); override;
  24. end;
  25. // ------------------------------------------------------------------
  26. implementation
  27. // ------------------------------------------------------------------
  28. procedure TessellatePolygon(PolyVerts: TgxAffineVectorList;
  29. PolyIndices, TriIndices: TgxIntegerList);
  30. function IsPolyClockWise: Boolean;
  31. var
  32. i, j: Integer;
  33. det: Single;
  34. mat: TAffineMatrix;
  35. begin
  36. det := 0;
  37. for i := 0 to PolyIndices.Count - 1 do
  38. begin
  39. for j := 0 to 2 do
  40. if (i + j) >= PolyIndices.Count then
  41. mat.V[j] := PolyVerts[PolyIndices[i + j - PolyIndices.Count]]
  42. else
  43. mat.V[j] := PolyVerts[PolyIndices[i + j]];
  44. det := det + MatrixDeterminant(mat);
  45. end;
  46. Result := (det < 0);
  47. end;
  48. function IsTriClockWise(v0, v1, v2: TAffineVector): Boolean;
  49. var
  50. mat: TAffineMatrix;
  51. begin
  52. mat.X := v0;
  53. mat.Y := v1;
  54. mat.Z := v2;
  55. Result := (MatrixDeterminant(mat) < 0);
  56. end;
  57. function PointInTriangle(p, v0, v1, v2: TAffineVector;
  58. IsClockWise: Boolean = False): Boolean;
  59. begin
  60. Result := not((IsTriClockWise(v1, v0, p) = IsClockWise) or
  61. (IsTriClockWise(v0, v2, p) = IsClockWise) or
  62. (IsTriClockWise(v2, v1, p) = IsClockWise));
  63. end;
  64. var
  65. i, j, prev, next, min_vert, min_prev, min_next: Integer;
  66. PolyCW, NoPointsInTriangle: Boolean;
  67. V: TAffineMatrix;
  68. temp: TgxIntegerList;
  69. min_dist, d, area: Single;
  70. begin
  71. temp := TgxIntegerList.Create;
  72. try
  73. PolyCW := IsPolyClockWise;
  74. temp.Assign(PolyIndices);
  75. while temp.Count > 3 do
  76. begin
  77. min_dist := 10E7;
  78. min_vert := -1;
  79. min_prev := -1;
  80. min_next := -1;
  81. for i := 0 to temp.Count - 1 do
  82. begin
  83. prev := i - 1;
  84. next := i + 1;
  85. if prev < 0 then
  86. prev := temp.Count - 1;
  87. if next > temp.Count - 1 then
  88. next := 0;
  89. V.X := PolyVerts[temp[prev]];
  90. V.Y := PolyVerts[temp[i]];
  91. V.Z := PolyVerts[temp[next]];
  92. if IsTriClockWise(V.X, V.Y, V.Z) = PolyCW then
  93. begin
  94. NoPointsInTriangle := True;
  95. for j := 0 to temp.Count - 1 do
  96. begin
  97. if (j <> i) and (j <> prev) and (j <> next) then
  98. begin
  99. if PointInTriangle(PolyVerts[temp[j]], V.X, V.Y, V.Z, PolyCW) then
  100. begin
  101. NoPointsInTriangle := False;
  102. Break;
  103. end;
  104. end;
  105. end;
  106. area := TriangleArea(V.X, V.Y, V.Z);
  107. if NoPointsInTriangle and (area > 0) then
  108. begin
  109. d := VectorDistance2(V.X, V.Z);
  110. if d < min_dist then
  111. begin
  112. min_dist := d;
  113. min_prev := prev;
  114. min_vert := i;
  115. min_next := next;
  116. end;
  117. end;
  118. end;
  119. end;
  120. if min_vert = -1 then
  121. begin
  122. raise Exception.Create('Failed to tessellate polygon.');
  123. end
  124. else
  125. begin
  126. TriIndices.Add(temp[min_prev], temp[min_vert], temp[min_next]);
  127. temp.Delete(min_vert);
  128. end;
  129. end;
  130. TriIndices.Add(temp[0], temp[1], temp[2]);
  131. finally
  132. temp.Free;
  133. end;
  134. end;
  135. // ------------------
  136. // ------------------ TgxVRMLVectorFile ------------------
  137. // ------------------
  138. class function TgxVRMLVectorFile.Capabilities: TDataFileCapabilities;
  139. begin
  140. Result := [dfcRead];
  141. end;
  142. procedure TgxVRMLVectorFile.LoadFromStream(aStream: TStream);
  143. var
  144. mesh: TgxMeshObject;
  145. uniqueMatID: Integer;
  146. currentMaterial: TgxLibMaterial;
  147. currentTransform: TMatrix4f;
  148. creaseAngle: Single;
  149. function GetUniqueMaterialName: String;
  150. var
  151. libMat: TgxLibMaterial;
  152. begin
  153. repeat
  154. Result := 'UntitledMaterial' + IntToStr(uniqueMatID);
  155. Inc(uniqueMatID);
  156. libMat := Owner.MaterialLibrary.Materials.GetLibMaterialByName(Result);
  157. until not Assigned(libMat);
  158. end;
  159. function AddMaterialToLibrary(VRMLMaterial: TVRMLMaterial): TgxLibMaterial;
  160. var
  161. matname: String;
  162. begin
  163. Result := nil;
  164. if not Assigned(Owner.MaterialLibrary) then
  165. Exit;
  166. if VRMLMaterial.DefName = '' then
  167. matname := GetUniqueMaterialName
  168. else
  169. matname := VRMLMaterial.DefName;
  170. Result := Owner.MaterialLibrary.Materials.GetLibMaterialByName(matname);
  171. if not Assigned(Result) then
  172. begin
  173. Result := Owner.MaterialLibrary.Materials.Add;
  174. Result.Name := matname;
  175. end;
  176. // Assign values from the current material
  177. if Assigned(currentMaterial) then
  178. Result.Material.FrontProperties.Assign
  179. (currentMaterial.Material.FrontProperties);
  180. with Result.Material.FrontProperties do
  181. begin
  182. if VRMLMaterial.HasDiffuse then
  183. Diffuse.Color := VectorMake(VRMLMaterial.DiffuseColor, Diffuse.Color.W);
  184. if VRMLMaterial.HasAmbient then
  185. Ambient.Color := VectorMake(VRMLMaterial.AmbientColor, Ambient.Color.W);
  186. if VRMLMaterial.HasSpecular then
  187. Specular.Color := VectorMake(VRMLMaterial.SpecularColor,
  188. Specular.Color.W);
  189. if VRMLMaterial.HasEmissive then
  190. Emission.Color := VectorMake(VRMLMaterial.EmissiveColor,
  191. Emission.Color.W);
  192. if Shininess = 0 then
  193. Shininess := 16;
  194. if VRMLMaterial.HasShininess then
  195. Shininess := Floor(128 * VRMLMaterial.Shininess);
  196. if VRMLMaterial.HasTransparency then
  197. begin
  198. Diffuse.Color := VectorMake(AffineVectorMake(Diffuse.Color),
  199. 1 - VRMLMaterial.Transparency);
  200. Ambient.Color := VectorMake(AffineVectorMake(Ambient.Color),
  201. 1 - VRMLMaterial.Transparency);
  202. Specular.Color := VectorMake(AffineVectorMake(Specular.Color),
  203. 1 - VRMLMaterial.Transparency);
  204. Emission.Color := VectorMake(AffineVectorMake(Emission.Color),
  205. 1 - VRMLMaterial.Transparency);
  206. end;
  207. end;
  208. if VRMLMaterial.HasTransparency then
  209. Result.Material.BlendingMode := bmTransparency;
  210. end;
  211. procedure RebuildMesh;
  212. var
  213. i, j, k, l: Integer;
  214. newfg: TgxFGVertexIndexList;
  215. fg: TFGVertexNormalTexIndexList;
  216. vertices, normals, texcoords, triNormals, newVertices, newNormals,
  217. newTexCoords: TgxAffineVectorList;
  218. optimized: TgxIntegerList;
  219. cosAngle: Single;
  220. normal: TAffineVector;
  221. s, t: array [0 .. 2] of Integer;
  222. n: array [0 .. 2] of TgxIntegerList;
  223. smooth, hasVertices, hasNormals, hasNormalIndices, hasTexCoords,
  224. hasTexCoordIndices: Boolean;
  225. begin
  226. if not Assigned(mesh) then
  227. Exit;
  228. hasVertices := mesh.vertices.Count > 0;
  229. hasNormals := mesh.normals.Count > 0;
  230. hasTexCoords := mesh.texcoords.Count > 0;
  231. if not hasVertices then
  232. Exit;
  233. vertices := TgxAffineVectorList.Create;
  234. normals := TgxAffineVectorList.Create;
  235. texcoords := TgxAffineVectorList.Create;
  236. newVertices := TgxAffineVectorList.Create;
  237. newNormals := TgxAffineVectorList.Create;
  238. newTexCoords := TgxAffineVectorList.Create;
  239. triNormals := TgxAffineVectorList.Create;
  240. n[0] := TgxIntegerList.Create;
  241. n[1] := TgxIntegerList.Create;
  242. n[2] := TgxIntegerList.Create;
  243. for i := 0 to mesh.FaceGroups.Count - 1 do
  244. begin
  245. fg := TFGVertexNormalTexIndexList(mesh.FaceGroups[i]);
  246. hasNormalIndices := fg.NormalIndices.Count > 0;
  247. hasTexCoordIndices := fg.TexCoordIndices.Count > 0;
  248. vertices.Clear;
  249. normals.Clear;
  250. texcoords.Clear;
  251. triNormals.Clear;
  252. if not hasNormals then
  253. begin
  254. for j := 0 to (fg.VertexIndices.Count div 3) - 1 do
  255. begin
  256. normal := VectorCrossProduct
  257. (VectorNormalize(VectorSubtract(mesh.vertices[fg.VertexIndices[3 * j
  258. + 1]], mesh.vertices[fg.VertexIndices[3 * j]])),
  259. VectorNormalize(VectorSubtract(mesh.vertices[fg.VertexIndices[3 * j
  260. + 2]], mesh.vertices[fg.VertexIndices[3 * j]])));
  261. triNormals.Add(VectorNormalize(normal));
  262. end;
  263. end;
  264. for j := 0 to (fg.VertexIndices.Count div 3) - 1 do
  265. begin
  266. vertices.Add(mesh.vertices[fg.VertexIndices[3 * j]],
  267. mesh.vertices[fg.VertexIndices[3 * j + 1]],
  268. mesh.vertices[fg.VertexIndices[3 * j + 2]]);
  269. if hasNormals then
  270. begin
  271. if hasNormalIndices then
  272. begin
  273. normals.Add(mesh.normals[fg.NormalIndices[3 * j]],
  274. mesh.normals[fg.NormalIndices[3 * j + 1]],
  275. mesh.normals[fg.NormalIndices[3 * j + 2]]);
  276. end
  277. else
  278. begin
  279. normals.Add(mesh.normals[fg.VertexIndices[3 * j]],
  280. mesh.normals[fg.VertexIndices[3 * j + 1]],
  281. mesh.normals[fg.VertexIndices[3 * j + 2]]);
  282. end;
  283. end
  284. else
  285. begin
  286. // No normal data, generate the normals
  287. n[0].Clear;
  288. n[1].Clear;
  289. n[2].Clear;
  290. s[0] := fg.VertexIndices[3 * j];
  291. s[1] := fg.VertexIndices[3 * j + 1];
  292. s[2] := fg.VertexIndices[3 * j + 2];
  293. for k := 0 to (fg.VertexIndices.Count div 3) - 1 do
  294. if j <> k then
  295. begin
  296. t[0] := fg.VertexIndices[3 * k];
  297. t[1] := fg.VertexIndices[3 * k + 1];
  298. t[2] := fg.VertexIndices[3 * k + 2];
  299. if (s[0] = t[0]) or (s[0] = t[1]) or (s[0] = t[2]) then
  300. n[0].Add(k);
  301. if (s[1] = t[0]) or (s[1] = t[1]) or (s[1] = t[2]) then
  302. n[1].Add(k);
  303. if (s[2] = t[0]) or (s[2] = t[1]) or (s[2] = t[2]) then
  304. n[2].Add(k);
  305. end;
  306. for k := 0 to 2 do
  307. begin
  308. if n[k].Count > 0 then
  309. begin
  310. smooth := True;
  311. for l := 0 to n[k].Count - 1 do
  312. begin
  313. cosAngle := VectorAngleCosine(triNormals[j],
  314. triNormals[n[k][l]]);
  315. smooth := smooth and (cosAngle > Cos(creaseAngle));
  316. if not smooth then
  317. Break;
  318. end;
  319. if smooth then
  320. begin
  321. normal := triNormals[j];
  322. for l := 0 to n[k].Count - 1 do
  323. AddVector(normal, triNormals[n[k][l]]);
  324. ScaleVector(normal, 1 / (n[k].Count + 1));
  325. normals.Add(VectorNormalize(normal));
  326. end
  327. else
  328. normals.Add(triNormals[j]);
  329. end
  330. else
  331. begin
  332. normals.Add(triNormals[j]);
  333. end;
  334. end;
  335. end;
  336. if hasTexCoords then
  337. begin
  338. if hasTexCoordIndices then
  339. begin
  340. texcoords.Add(mesh.texcoords[fg.TexCoordIndices[3 * j]],
  341. mesh.texcoords[fg.TexCoordIndices[3 * j + 1]],
  342. mesh.texcoords[fg.TexCoordIndices[3 * j + 2]]);
  343. end
  344. else
  345. begin
  346. texcoords.Add(mesh.texcoords[fg.VertexIndices[3 * j]],
  347. mesh.texcoords[fg.VertexIndices[3 * j + 1]],
  348. mesh.texcoords[fg.VertexIndices[3 * j + 2]]);
  349. end;
  350. end;
  351. end;
  352. // Optimize the mesh
  353. if hasTexCoords then
  354. begin
  355. optimized := BuildVectorCountOptimizedIndices(vertices, normals,
  356. texcoords);
  357. RemapReferences(texcoords, optimized);
  358. end
  359. else
  360. optimized := BuildVectorCountOptimizedIndices(vertices, normals);
  361. RemapReferences(normals, optimized);
  362. RemapAndCleanupReferences(vertices, optimized);
  363. optimized.Offset(newVertices.Count);
  364. // Replace the facegroup with a vertex-only index list
  365. newfg := TgxFGVertexIndexList.Create;
  366. newfg.Owner := mesh.FaceGroups;
  367. newfg.Mode := fg.Mode;
  368. newfg.MaterialName := fg.MaterialName;
  369. newfg.VertexIndices.Assign(optimized);
  370. mesh.FaceGroups.Insert(i, newfg);
  371. mesh.FaceGroups.RemoveAndFree(fg);
  372. optimized.Free;
  373. newVertices.Add(vertices);
  374. newNormals.Add(normals);
  375. newTexCoords.Add(texcoords);
  376. end;
  377. vertices.Free;
  378. normals.Free;
  379. texcoords.Free;
  380. n[0].Free;
  381. n[1].Free;
  382. n[2].Free;
  383. triNormals.Free;
  384. if newVertices.Count > 0 then
  385. mesh.vertices.Assign(newVertices);
  386. if newNormals.Count > 0 then
  387. mesh.normals.Assign(newNormals);
  388. if newTexCoords.Count > 0 then
  389. mesh.texcoords.Assign(newTexCoords);
  390. newVertices.Free;
  391. newNormals.Free;
  392. newTexCoords.Free;
  393. end;
  394. procedure RecursNodes(node: TVRMLNode);
  395. var
  396. i, j, n: Integer;
  397. points: TgxSingleList;
  398. indices, fgindices: TgxIntegerList;
  399. fg: TFGVertexNormalTexIndexList;
  400. face: TgxIntegerList;
  401. tempLibMat: TgxLibMaterial;
  402. saveTransform, mat: TMatrix4f;
  403. saveMaterial: TgxLibMaterial;
  404. axis: TAffineVector;
  405. angle: Single;
  406. begin
  407. // Store current transform and material
  408. saveTransform := currentTransform;
  409. saveMaterial := currentMaterial;
  410. // Look for a child node data (transforms and materials)
  411. for i := 0 to node.Count - 1 do
  412. if node[i] is TVRMLTransform then
  413. begin
  414. if not VectorEquals(TVRMLTransform(node[i]).Rotation, NullHMGVector)
  415. then
  416. begin
  417. axis := AffineVectorMake(TVRMLTransform(node[i]).Rotation);
  418. angle := TVRMLTransform(node[i]).Rotation.W;
  419. mat := MatrixMultiply(CreateRotationMatrix(axis, angle),
  420. CreateRotationMatrixZ(Pi / 2));
  421. end
  422. else
  423. mat := IdentityHMGMatrix;
  424. for j := 0 to 2 do
  425. mat.V[j] := VectorScale(mat.V[j], TVRMLTransform(node[i])
  426. .ScaleFactor.V[j]);
  427. mat.W := PointMake(TVRMLTransform(node[i]).Center);
  428. currentTransform := MatrixMultiply(mat, currentTransform);
  429. end
  430. else if node[i] is TVRMLMaterial then
  431. begin
  432. currentMaterial := AddMaterialToLibrary(TVRMLMaterial(node[i]));
  433. end
  434. else if node[i] is TVRMLShapeHints then
  435. begin
  436. creaseAngle := TVRMLShapeHints(node[i]).creaseAngle;
  437. end
  438. else if node[i] is TVRMLUse then
  439. begin
  440. if Assigned(Owner.MaterialLibrary) then
  441. begin
  442. tempLibMat := Owner.MaterialLibrary.Materials.GetLibMaterialByName
  443. (TVRMLUse(node[i]).Value);
  444. if Assigned(tempLibMat) then
  445. currentMaterial := tempLibMat;
  446. end;
  447. end;
  448. // Read node data
  449. if (node.Name = 'Coordinate3') and (node.Count > 0) then
  450. begin
  451. RebuildMesh;
  452. mesh := TgxMeshObject.CreateOwned(Owner.MeshObjects);
  453. points := TVRMLSingleArray(node[0]).Values;
  454. for i := 0 to (points.Count div 3) - 1 do
  455. mesh.vertices.Add(points[3 * i], points[3 * i + 1], points[3 * i + 2]);
  456. mesh.vertices.TransformAsPoints(currentTransform);
  457. end
  458. else if (node.Name = 'Normal') and (node.Count > 0) and Assigned(mesh) then
  459. begin
  460. points := TVRMLSingleArray(node[0]).Values;
  461. for i := 0 to (points.Count div 3) - 1 do
  462. mesh.normals.Add(points[3 * i], points[3 * i + 1], points[3 * i + 2]);
  463. mesh.normals.TransformAsVectors(currentTransform);
  464. end
  465. else if (node.Name = 'TextureCoordinate2') and (node.Count > 0) and
  466. Assigned(mesh) then
  467. begin
  468. points := TVRMLSingleArray(node[0]).Values;
  469. for i := 0 to (points.Count div 2) - 1 do
  470. mesh.texcoords.Add(points[2 * i], points[2 * i + 1], 0);
  471. end
  472. else if (node.Name = 'IndexedFaceSet') and (node.Count > 0) and
  473. Assigned(mesh) then
  474. begin
  475. fg := TFGVertexNormalTexIndexList.CreateOwned(mesh.FaceGroups);
  476. mesh.Mode := momFaceGroups;
  477. face := TgxIntegerList.Create;
  478. if Assigned(currentMaterial) then
  479. fg.MaterialName := currentMaterial.Name;
  480. for n := 0 to node.Count - 1 do
  481. begin
  482. if node[n].Name = 'CoordIndexArray' then
  483. fgindices := fg.VertexIndices
  484. else if node[n].Name = 'NormalIndexArray' then
  485. fgindices := fg.NormalIndices
  486. else if node[n].Name = 'TextureCoordIndexArray' then
  487. fgindices := fg.TexCoordIndices
  488. else
  489. fgindices := nil;
  490. if not Assigned(fgindices) then
  491. Continue;
  492. indices := TVRMLIntegerArray(node[n]).Values;
  493. i := 0;
  494. while i < indices.Count do
  495. begin
  496. if indices[i] = -1 then
  497. begin
  498. if face.Count <= 4 then
  499. begin
  500. for j := 0 to face.Count - 3 do
  501. fgindices.Add(face[0], face[j + 1], face[j + 2]);
  502. end
  503. else
  504. begin
  505. TessellatePolygon(mesh.vertices, face, fgindices);
  506. end;
  507. face.Clear;
  508. end
  509. else
  510. begin
  511. face.Add(indices[i]);
  512. end;
  513. i := i + 1;
  514. end;
  515. end;
  516. face.Free;
  517. end
  518. else
  519. begin
  520. // Continue recursion through child nodes
  521. for i := 0 to node.Count - 1 do
  522. RecursNodes(node[i]);
  523. end;
  524. // Load transform and material from stored values
  525. currentTransform := saveTransform;
  526. currentMaterial := saveMaterial;
  527. end;
  528. var
  529. str: TStringList;
  530. parser: TVRMLParser;
  531. begin
  532. str := TStringList.Create;
  533. parser := TVRMLParser.Create;
  534. currentMaterial := nil;
  535. currentTransform := IdentityHMGMatrix;
  536. creaseAngle := 0.5;
  537. mesh := nil;
  538. uniqueMatID := 0;
  539. try
  540. str.LoadFromStream(aStream);
  541. parser.Parse(str.Text);
  542. currentMaterial := nil;
  543. RecursNodes(parser.RootNode);
  544. RebuildMesh;
  545. finally
  546. str.Free;
  547. parser.Free;
  548. end;
  549. end;
  550. // ------------------------------------------------------------------
  551. initialization
  552. // ------------------------------------------------------------------
  553. RegisterVectorFileFormat('wrl', 'VRML files', TgxVRMLVectorFile);
  554. end.