123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614 |
- //
- // The graphics engine GXScene https://github.com/glscene
- //
- unit GXS.FileVRML;
- (* Preliminary VRML vector file support *)
- interface
- uses
- System.Classes,
- System.SysUtils,
- System.Math,
- Stage.VectorTypes,
- Stage.VectorGeometry,
- GXS.VectorLists,
- GXS.ApplicationFileIO,
- GXS.VectorFileObjects,
- GXS.Material,
- GXS.MeshUtils,
- Formatx.VRML;
- type
- TgxVRMLVectorFile = class(TgxVectorFile)
- public
- class function Capabilities: TDataFileCapabilities; override;
- procedure LoadFromStream(aStream: TStream); override;
- end;
- // ------------------------------------------------------------------
- implementation
- // ------------------------------------------------------------------
- procedure TessellatePolygon(PolyVerts: TgxAffineVectorList;
- PolyIndices, TriIndices: TgxIntegerList);
- function IsPolyClockWise: Boolean;
- var
- i, j: Integer;
- det: Single;
- mat: TAffineMatrix;
- begin
- det := 0;
- for i := 0 to PolyIndices.Count - 1 do
- begin
- for j := 0 to 2 do
- if (i + j) >= PolyIndices.Count then
- mat.V[j] := PolyVerts[PolyIndices[i + j - PolyIndices.Count]]
- else
- mat.V[j] := PolyVerts[PolyIndices[i + j]];
- det := det + MatrixDeterminant(mat);
- end;
- Result := (det < 0);
- end;
- function IsTriClockWise(v0, v1, v2: TAffineVector): Boolean;
- var
- mat: TAffineMatrix;
- begin
- mat.X := v0;
- mat.Y := v1;
- mat.Z := v2;
- Result := (MatrixDeterminant(mat) < 0);
- end;
- function PointInTriangle(p, v0, v1, v2: TAffineVector;
- IsClockWise: Boolean = False): Boolean;
- begin
- Result := not((IsTriClockWise(v1, v0, p) = IsClockWise) or
- (IsTriClockWise(v0, v2, p) = IsClockWise) or
- (IsTriClockWise(v2, v1, p) = IsClockWise));
- end;
- var
- i, j, prev, next, min_vert, min_prev, min_next: Integer;
- PolyCW, NoPointsInTriangle: Boolean;
- V: TAffineMatrix;
- temp: TgxIntegerList;
- min_dist, d, area: Single;
- begin
- temp := TgxIntegerList.Create;
- try
- PolyCW := IsPolyClockWise;
- temp.Assign(PolyIndices);
- while temp.Count > 3 do
- begin
- min_dist := 10E7;
- min_vert := -1;
- min_prev := -1;
- min_next := -1;
- for i := 0 to temp.Count - 1 do
- begin
- prev := i - 1;
- next := i + 1;
- if prev < 0 then
- prev := temp.Count - 1;
- if next > temp.Count - 1 then
- next := 0;
- V.X := PolyVerts[temp[prev]];
- V.Y := PolyVerts[temp[i]];
- V.Z := PolyVerts[temp[next]];
- if IsTriClockWise(V.X, V.Y, V.Z) = PolyCW then
- begin
- NoPointsInTriangle := True;
- for j := 0 to temp.Count - 1 do
- begin
- if (j <> i) and (j <> prev) and (j <> next) then
- begin
- if PointInTriangle(PolyVerts[temp[j]], V.X, V.Y, V.Z, PolyCW) then
- begin
- NoPointsInTriangle := False;
- Break;
- end;
- end;
- end;
- area := TriangleArea(V.X, V.Y, V.Z);
- if NoPointsInTriangle and (area > 0) then
- begin
- d := VectorDistance2(V.X, V.Z);
- if d < min_dist then
- begin
- min_dist := d;
- min_prev := prev;
- min_vert := i;
- min_next := next;
- end;
- end;
- end;
- end;
- if min_vert = -1 then
- begin
- raise Exception.Create('Failed to tessellate polygon.');
- end
- else
- begin
- TriIndices.Add(temp[min_prev], temp[min_vert], temp[min_next]);
- temp.Delete(min_vert);
- end;
- end;
- TriIndices.Add(temp[0], temp[1], temp[2]);
- finally
- temp.Free;
- end;
- end;
- // ------------------
- // ------------------ TgxVRMLVectorFile ------------------
- // ------------------
- class function TgxVRMLVectorFile.Capabilities: TDataFileCapabilities;
- begin
- Result := [dfcRead];
- end;
- procedure TgxVRMLVectorFile.LoadFromStream(aStream: TStream);
- var
- mesh: TgxMeshObject;
- uniqueMatID: Integer;
- currentMaterial: TgxLibMaterial;
- currentTransform: TMatrix4f;
- creaseAngle: Single;
- function GetUniqueMaterialName: String;
- var
- libMat: TgxLibMaterial;
- begin
- repeat
- Result := 'UntitledMaterial' + IntToStr(uniqueMatID);
- Inc(uniqueMatID);
- libMat := Owner.MaterialLibrary.Materials.GetLibMaterialByName(Result);
- until not Assigned(libMat);
- end;
- function AddMaterialToLibrary(VRMLMaterial: TVRMLMaterial): TgxLibMaterial;
- var
- matname: String;
- begin
- Result := nil;
- if not Assigned(Owner.MaterialLibrary) then
- Exit;
- if VRMLMaterial.DefName = '' then
- matname := GetUniqueMaterialName
- else
- matname := VRMLMaterial.DefName;
- Result := Owner.MaterialLibrary.Materials.GetLibMaterialByName(matname);
- if not Assigned(Result) then
- begin
- Result := Owner.MaterialLibrary.Materials.Add;
- Result.Name := matname;
- end;
- // Assign values from the current material
- if Assigned(currentMaterial) then
- Result.Material.FrontProperties.Assign
- (currentMaterial.Material.FrontProperties);
- with Result.Material.FrontProperties do
- begin
- if VRMLMaterial.HasDiffuse then
- Diffuse.Color := VectorMake(VRMLMaterial.DiffuseColor, Diffuse.Color.W);
- if VRMLMaterial.HasAmbient then
- Ambient.Color := VectorMake(VRMLMaterial.AmbientColor, Ambient.Color.W);
- if VRMLMaterial.HasSpecular then
- Specular.Color := VectorMake(VRMLMaterial.SpecularColor,
- Specular.Color.W);
- if VRMLMaterial.HasEmissive then
- Emission.Color := VectorMake(VRMLMaterial.EmissiveColor,
- Emission.Color.W);
- if Shininess = 0 then
- Shininess := 16;
- if VRMLMaterial.HasShininess then
- Shininess := Floor(128 * VRMLMaterial.Shininess);
- if VRMLMaterial.HasTransparency then
- begin
- Diffuse.Color := VectorMake(AffineVectorMake(Diffuse.Color),
- 1 - VRMLMaterial.Transparency);
- Ambient.Color := VectorMake(AffineVectorMake(Ambient.Color),
- 1 - VRMLMaterial.Transparency);
- Specular.Color := VectorMake(AffineVectorMake(Specular.Color),
- 1 - VRMLMaterial.Transparency);
- Emission.Color := VectorMake(AffineVectorMake(Emission.Color),
- 1 - VRMLMaterial.Transparency);
- end;
- end;
- if VRMLMaterial.HasTransparency then
- Result.Material.BlendingMode := bmTransparency;
- end;
- procedure RebuildMesh;
- var
- i, j, k, l: Integer;
- newfg: TgxFGVertexIndexList;
- fg: TFGVertexNormalTexIndexList;
- vertices, normals, texcoords, triNormals, newVertices, newNormals,
- newTexCoords: TgxAffineVectorList;
- optimized: TgxIntegerList;
- cosAngle: Single;
- normal: TAffineVector;
- s, t: array [0 .. 2] of Integer;
- n: array [0 .. 2] of TgxIntegerList;
- smooth, hasVertices, hasNormals, hasNormalIndices, hasTexCoords,
- hasTexCoordIndices: Boolean;
- begin
- if not Assigned(mesh) then
- Exit;
- hasVertices := mesh.vertices.Count > 0;
- hasNormals := mesh.normals.Count > 0;
- hasTexCoords := mesh.texcoords.Count > 0;
- if not hasVertices then
- Exit;
- vertices := TgxAffineVectorList.Create;
- normals := TgxAffineVectorList.Create;
- texcoords := TgxAffineVectorList.Create;
- newVertices := TgxAffineVectorList.Create;
- newNormals := TgxAffineVectorList.Create;
- newTexCoords := TgxAffineVectorList.Create;
- triNormals := TgxAffineVectorList.Create;
- n[0] := TgxIntegerList.Create;
- n[1] := TgxIntegerList.Create;
- n[2] := TgxIntegerList.Create;
- for i := 0 to mesh.FaceGroups.Count - 1 do
- begin
- fg := TFGVertexNormalTexIndexList(mesh.FaceGroups[i]);
- hasNormalIndices := fg.NormalIndices.Count > 0;
- hasTexCoordIndices := fg.TexCoordIndices.Count > 0;
- vertices.Clear;
- normals.Clear;
- texcoords.Clear;
- triNormals.Clear;
- if not hasNormals then
- begin
- for j := 0 to (fg.VertexIndices.Count div 3) - 1 do
- begin
- normal := VectorCrossProduct
- (VectorNormalize(VectorSubtract(mesh.vertices[fg.VertexIndices[3 * j
- + 1]], mesh.vertices[fg.VertexIndices[3 * j]])),
- VectorNormalize(VectorSubtract(mesh.vertices[fg.VertexIndices[3 * j
- + 2]], mesh.vertices[fg.VertexIndices[3 * j]])));
- triNormals.Add(VectorNormalize(normal));
- end;
- end;
- for j := 0 to (fg.VertexIndices.Count div 3) - 1 do
- begin
- vertices.Add(mesh.vertices[fg.VertexIndices[3 * j]],
- mesh.vertices[fg.VertexIndices[3 * j + 1]],
- mesh.vertices[fg.VertexIndices[3 * j + 2]]);
- if hasNormals then
- begin
- if hasNormalIndices then
- begin
- normals.Add(mesh.normals[fg.NormalIndices[3 * j]],
- mesh.normals[fg.NormalIndices[3 * j + 1]],
- mesh.normals[fg.NormalIndices[3 * j + 2]]);
- end
- else
- begin
- normals.Add(mesh.normals[fg.VertexIndices[3 * j]],
- mesh.normals[fg.VertexIndices[3 * j + 1]],
- mesh.normals[fg.VertexIndices[3 * j + 2]]);
- end;
- end
- else
- begin
- // No normal data, generate the normals
- n[0].Clear;
- n[1].Clear;
- n[2].Clear;
- s[0] := fg.VertexIndices[3 * j];
- s[1] := fg.VertexIndices[3 * j + 1];
- s[2] := fg.VertexIndices[3 * j + 2];
- for k := 0 to (fg.VertexIndices.Count div 3) - 1 do
- if j <> k then
- begin
- t[0] := fg.VertexIndices[3 * k];
- t[1] := fg.VertexIndices[3 * k + 1];
- t[2] := fg.VertexIndices[3 * k + 2];
- if (s[0] = t[0]) or (s[0] = t[1]) or (s[0] = t[2]) then
- n[0].Add(k);
- if (s[1] = t[0]) or (s[1] = t[1]) or (s[1] = t[2]) then
- n[1].Add(k);
- if (s[2] = t[0]) or (s[2] = t[1]) or (s[2] = t[2]) then
- n[2].Add(k);
- end;
- for k := 0 to 2 do
- begin
- if n[k].Count > 0 then
- begin
- smooth := True;
- for l := 0 to n[k].Count - 1 do
- begin
- cosAngle := VectorAngleCosine(triNormals[j],
- triNormals[n[k][l]]);
- smooth := smooth and (cosAngle > Cos(creaseAngle));
- if not smooth then
- Break;
- end;
- if smooth then
- begin
- normal := triNormals[j];
- for l := 0 to n[k].Count - 1 do
- AddVector(normal, triNormals[n[k][l]]);
- ScaleVector(normal, 1 / (n[k].Count + 1));
- normals.Add(VectorNormalize(normal));
- end
- else
- normals.Add(triNormals[j]);
- end
- else
- begin
- normals.Add(triNormals[j]);
- end;
- end;
- end;
- if hasTexCoords then
- begin
- if hasTexCoordIndices then
- begin
- texcoords.Add(mesh.texcoords[fg.TexCoordIndices[3 * j]],
- mesh.texcoords[fg.TexCoordIndices[3 * j + 1]],
- mesh.texcoords[fg.TexCoordIndices[3 * j + 2]]);
- end
- else
- begin
- texcoords.Add(mesh.texcoords[fg.VertexIndices[3 * j]],
- mesh.texcoords[fg.VertexIndices[3 * j + 1]],
- mesh.texcoords[fg.VertexIndices[3 * j + 2]]);
- end;
- end;
- end;
- // Optimize the mesh
- if hasTexCoords then
- begin
- optimized := BuildVectorCountOptimizedIndices(vertices, normals,
- texcoords);
- RemapReferences(texcoords, optimized);
- end
- else
- optimized := BuildVectorCountOptimizedIndices(vertices, normals);
- RemapReferences(normals, optimized);
- RemapAndCleanupReferences(vertices, optimized);
- optimized.Offset(newVertices.Count);
- // Replace the facegroup with a vertex-only index list
- newfg := TgxFGVertexIndexList.Create;
- newfg.Owner := mesh.FaceGroups;
- newfg.Mode := fg.Mode;
- newfg.MaterialName := fg.MaterialName;
- newfg.VertexIndices.Assign(optimized);
- mesh.FaceGroups.Insert(i, newfg);
- mesh.FaceGroups.RemoveAndFree(fg);
- optimized.Free;
- newVertices.Add(vertices);
- newNormals.Add(normals);
- newTexCoords.Add(texcoords);
- end;
- vertices.Free;
- normals.Free;
- texcoords.Free;
- n[0].Free;
- n[1].Free;
- n[2].Free;
- triNormals.Free;
- if newVertices.Count > 0 then
- mesh.vertices.Assign(newVertices);
- if newNormals.Count > 0 then
- mesh.normals.Assign(newNormals);
- if newTexCoords.Count > 0 then
- mesh.texcoords.Assign(newTexCoords);
- newVertices.Free;
- newNormals.Free;
- newTexCoords.Free;
- end;
- procedure RecursNodes(node: TVRMLNode);
- var
- i, j, n: Integer;
- points: TgxSingleList;
- indices, fgindices: TgxIntegerList;
- fg: TFGVertexNormalTexIndexList;
- face: TgxIntegerList;
- tempLibMat: TgxLibMaterial;
- saveTransform, mat: TMatrix4f;
- saveMaterial: TgxLibMaterial;
- axis: TAffineVector;
- angle: Single;
- begin
- // Store current transform and material
- saveTransform := currentTransform;
- saveMaterial := currentMaterial;
- // Look for a child node data (transforms and materials)
- for i := 0 to node.Count - 1 do
- if node[i] is TVRMLTransform then
- begin
- if not VectorEquals(TVRMLTransform(node[i]).Rotation, NullHMGVector)
- then
- begin
- axis := AffineVectorMake(TVRMLTransform(node[i]).Rotation);
- angle := TVRMLTransform(node[i]).Rotation.W;
- mat := MatrixMultiply(CreateRotationMatrix(axis, angle),
- CreateRotationMatrixZ(Pi / 2));
- end
- else
- mat := IdentityHMGMatrix;
- for j := 0 to 2 do
- mat.V[j] := VectorScale(mat.V[j], TVRMLTransform(node[i])
- .ScaleFactor.V[j]);
- mat.W := PointMake(TVRMLTransform(node[i]).Center);
- currentTransform := MatrixMultiply(mat, currentTransform);
- end
- else if node[i] is TVRMLMaterial then
- begin
- currentMaterial := AddMaterialToLibrary(TVRMLMaterial(node[i]));
- end
- else if node[i] is TVRMLShapeHints then
- begin
- creaseAngle := TVRMLShapeHints(node[i]).creaseAngle;
- end
- else if node[i] is TVRMLUse then
- begin
- if Assigned(Owner.MaterialLibrary) then
- begin
- tempLibMat := Owner.MaterialLibrary.Materials.GetLibMaterialByName
- (TVRMLUse(node[i]).Value);
- if Assigned(tempLibMat) then
- currentMaterial := tempLibMat;
- end;
- end;
- // Read node data
- if (node.Name = 'Coordinate3') and (node.Count > 0) then
- begin
- RebuildMesh;
- mesh := TgxMeshObject.CreateOwned(Owner.MeshObjects);
- points := TVRMLSingleArray(node[0]).Values;
- for i := 0 to (points.Count div 3) - 1 do
- mesh.vertices.Add(points[3 * i], points[3 * i + 1], points[3 * i + 2]);
- mesh.vertices.TransformAsPoints(currentTransform);
- end
- else if (node.Name = 'Normal') and (node.Count > 0) and Assigned(mesh) then
- begin
- points := TVRMLSingleArray(node[0]).Values;
- for i := 0 to (points.Count div 3) - 1 do
- mesh.normals.Add(points[3 * i], points[3 * i + 1], points[3 * i + 2]);
- mesh.normals.TransformAsVectors(currentTransform);
- end
- else if (node.Name = 'TextureCoordinate2') and (node.Count > 0) and
- Assigned(mesh) then
- begin
- points := TVRMLSingleArray(node[0]).Values;
- for i := 0 to (points.Count div 2) - 1 do
- mesh.texcoords.Add(points[2 * i], points[2 * i + 1], 0);
- end
- else if (node.Name = 'IndexedFaceSet') and (node.Count > 0) and
- Assigned(mesh) then
- begin
- fg := TFGVertexNormalTexIndexList.CreateOwned(mesh.FaceGroups);
- mesh.Mode := momFaceGroups;
- face := TgxIntegerList.Create;
- if Assigned(currentMaterial) then
- fg.MaterialName := currentMaterial.Name;
- for n := 0 to node.Count - 1 do
- begin
- if node[n].Name = 'CoordIndexArray' then
- fgindices := fg.VertexIndices
- else if node[n].Name = 'NormalIndexArray' then
- fgindices := fg.NormalIndices
- else if node[n].Name = 'TextureCoordIndexArray' then
- fgindices := fg.TexCoordIndices
- else
- fgindices := nil;
- if not Assigned(fgindices) then
- Continue;
- indices := TVRMLIntegerArray(node[n]).Values;
- i := 0;
- while i < indices.Count do
- begin
- if indices[i] = -1 then
- begin
- if face.Count <= 4 then
- begin
- for j := 0 to face.Count - 3 do
- fgindices.Add(face[0], face[j + 1], face[j + 2]);
- end
- else
- begin
- TessellatePolygon(mesh.vertices, face, fgindices);
- end;
- face.Clear;
- end
- else
- begin
- face.Add(indices[i]);
- end;
- i := i + 1;
- end;
- end;
- face.Free;
- end
- else
- begin
- // Continue recursion through child nodes
- for i := 0 to node.Count - 1 do
- RecursNodes(node[i]);
- end;
- // Load transform and material from stored values
- currentTransform := saveTransform;
- currentMaterial := saveMaterial;
- end;
- var
- str: TStringList;
- parser: TVRMLParser;
- begin
- str := TStringList.Create;
- parser := TVRMLParser.Create;
- currentMaterial := nil;
- currentTransform := IdentityHMGMatrix;
- creaseAngle := 0.5;
- mesh := nil;
- uniqueMatID := 0;
- try
- str.LoadFromStream(aStream);
- parser.Parse(str.Text);
- currentMaterial := nil;
- RecursNodes(parser.RootNode);
- RebuildMesh;
- finally
- str.Free;
- parser.Free;
- end;
- end;
- // ------------------------------------------------------------------
- initialization
- // ------------------------------------------------------------------
- RegisterVectorFileFormat('wrl', 'VRML files', TgxVRMLVectorFile);
- end.
|