123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283 |
- //
- // The graphics engine GXScene https://github.com/glscene
- //
- unit GXS.FileQ3BSP;
- (*
- Support-code to load Q3BSP Files into TgxFreeForm-Components.
- Note that you must manually add this unit to one of your project's uses
- to enable support for OBJ & OBJF at run-time.
- *)
- interface
- {$I Stage.Defines.inc}
- uses
- System.Classes,
- System.SysUtils,
- FMX.Graphics,
- GXS.VectorFileObjects,
- GXS.ApplicationFileIO,
- Stage.VectorGeometry,
- Stage.VectorTypes,
- GXS.VectorLists,
- GXS.BSP,
- GXS.Texture,
- GXS.Graphics,
- GXS.State,
- GXS.ImageUtils,
- GXS.Material,
- Stage.TextureFormat,
- Formatx.Q3BSP;
- type
- // The Q3BSP vector file (Quake III BSP).
- TgxQ3BSPVectorFile = class(TgxVectorFile)
- public
- class function Capabilities: TDataFileCapabilities; override;
- procedure LoadFromStream(aStream: TStream); override;
- end;
- var
- // Q3 lightmaps are quite dark, we brighten them a lot by default
- vQ3BSPLightmapGammaCorrection: Single = 2.5;
- vQ3BSPLightmapBrightness: Single = 2; // scaling factor, 1.0 = unchanged
- vFileQ3BSPLoadMaterials: boolean = True; // Mrqzzz : Flag to avoid loading materials (useful for IDE Extentions like GlaredX)
- // ------------------------------------------------------------------
- implementation
- // ------------------------------------------------------------------
- // ------------------
- // ------------------ TGXSTLVectorFile ------------------
- // ------------------
- class function TgxQ3BSPVectorFile.Capabilities: TDataFileCapabilities;
- begin
- Result := [dfcRead];
- end;
- procedure TgxQ3BSPVectorFile.LoadFromStream(aStream: TStream);
- function LocateTextureFile(const texName: string): string;
- begin
- if FileStreamExists(texName + '.bmp') then
- Result := texName + '.bmp'
- else if FileStreamExists(texName + '.jpg') then
- Result := texName + '.jpg'
- else if FileStreamExists(texName + '.tga') then
- Result := texName + '.tga'
- else
- Result := '';
- end;
- function GetOrAllocateMaterial(const matName: string): string;
- var
- matLib: TgxMaterialLibrary;
- libMat: TgxLibMaterial;
- texName: string;
- begin
- if GetOwner is TgxBaseMesh then
- begin
- // got a linked material library?
- matLib := TgxBaseMesh(GetOwner).MaterialLibrary;
- if Assigned(matLib) then
- begin
- Result := matName;
- libMat := matLib.Materials.GetLibMaterialByName(matName);
- if not Assigned(libMat) then
- begin
- if Pos('.', matName) < 1 then
- begin
- texName := LocateTextureFile(matName);
- if texName = '' then
- texName := LocateTextureFile(Copy(matName, LastDelimiter('\/', matName) + 1, MaxInt));
- end
- else
- texName := matName;
- with matLib.AddTextureMaterial(matName, texName) do
- begin
- Material.Texture.TextureMode := tmModulate;
- end;
- end;
- end
- else
- Result := '';
- end
- else
- Result := '';
- end;
- var
- bsp: TQ3BSP;
- mo: TBSPMeshObject;
- fg, lastfg: TFGBSPNode;
- i, j, n, y: Integer;
- facePtr: PBSPFace;
- lightmapLib: TgxMaterialLibrary;
- lightmapBmp: TBitmap;
- libMat: TgxLibMaterial;
- bspLightMap: PBSPLightmap;
- plane: THmgPlane;
- begin
- bsp := TQ3BSP.Create(aStream);
- try
- mo := TBSPMeshObject.CreateOwned(Owner.MeshObjects);
- // import all materials
- if vFileQ3BSPLoadMaterials then
- begin
- for i := 0 to High(bsp.Textures) do
- begin
- GetOrAllocateMaterial(Trim(string(StrPas(bsp.Textures[i].TextureName))));
- end;
- end;
- // import all lightmaps
- lightmapLib := Owner.LightmapLibrary;
- if Assigned(lightmapLib) and vFileQ3BSPLoadMaterials then
- begin
- // import lightmaps
- n := bsp.NumOfLightmaps;
- lightmapBmp := TBitmap.Create;
- try
- { TODO : E2129 Cannot assign to a read-only property }
- (*lightmapBmp.PixelFormat := glpf24bit;*)
- lightmapBmp.Width := 128;
- lightmapBmp.Height := 128;
- for i := 0 to n - 1 do
- begin
- bspLightMap := @bsp.Lightmaps[i];
- // apply brightness correction if ant
- if vQ3BSPLightmapBrightness <> 1 then
- BrightenRGBArray(@bspLightMap.imageBits[0], 128 * 128,
- vQ3BSPLightmapBrightness);
- // apply gamma correction if any
- if vQ3BSPLightmapGammaCorrection <> 1 then
- GammaCorrectRGBArray(@bspLightMap.imageBits[0], 128 * 128,
- vQ3BSPLightmapGammaCorrection);
- // convert RAW RGB to BMP
- for y := 0 to 127 do
- { TODO : E2003 Undeclared identifier: 'ScanLine' }
- (*
- BGR24ToRGB24(@bspLightMap.imageBits[y * 128 * 3],
- lightmapBmp.ScanLine[127 - y], 128);
- *)
- // spawn lightmap
- libMat := lightmapLib.AddTextureMaterial(IntToStr(i), lightmapBmp);
- with libMat.Material.Texture do
- begin
- MinFilter := miLinear;
- TextureWrap := twNone;
- TextureFormat := tfRGB;
- end;
- end;
- finally
- lightmapBmp.Free;
- end;
- end;
- // import all geometry
- mo.Vertices.AdjustCapacityToAtLeast(bsp.NumOfVerts);
- mo.Normals.AdjustCapacityToAtLeast(bsp.NumOfVerts);
- mo.TexCoords.AdjustCapacityToAtLeast(bsp.NumOfVerts);
- for i := 0 to bsp.NumOfVerts - 1 do
- begin
- mo.Vertices.Add(bsp.Vertices[i].Position);
- mo.Normals.Add(bsp.Vertices[i].Normal);
- mo.TexCoords.Add(bsp.Vertices[i].TextureCoord);
- if Assigned(lightMapLib) and vFileQ3BSPLoadMaterials then
- mo.LightMapTexCoords.Add(bsp.Vertices[i].LightmapCoord)
- end;
- mo.TexCoords.Scale(AffineVectorMake(1, -1, 0));
- mo.TexCoords.Translate(YVector);
- mo.RenderSort := rsBackToFront;
- // Q3 BSP separates tree nodes from leafy nodes, we don't,
- // so we place nodes first, then all leafs afterwards
- for i := 0 to bsp.NumOfNodes - 1 do
- begin
- fg := TFGBSPNode.CreateOwned(mo.FaceGroups);
- plane := bsp.Planes[bsp.Nodes[i].plane];
- plane := VectorMake(plane.X, plane.Y, plane.Z, plane.W);
- fg.SplitPlane := plane;
- fg.PositiveSubNodeIndex := bsp.Nodes[i].Children[0];
- if fg.PositiveSubNodeIndex < 0 then
- fg.PositiveSubNodeIndex := bsp.NumOfNodes - fg.PositiveSubNodeIndex - 1;
- Assert(fg.PositiveSubNodeIndex < bsp.NumOfNodes + bsp.NumOfLeaves);
- Assert(fg.PositiveSubNodeIndex > 0);
- fg.NegativeSubNodeIndex := bsp.Nodes[i].Children[1];
- if fg.NegativeSubNodeIndex < 0 then
- fg.NegativeSubNodeIndex := bsp.NumOfNodes - fg.NegativeSubNodeIndex - 1;
- Assert(fg.NegativeSubNodeIndex < bsp.NumOfNodes + bsp.NumOfLeaves);
- Assert(fg.NegativeSubNodeIndex > 0);
- end;
- // import all leaves
- for i := 0 to bsp.NumOfLeaves - 1 do
- TFGBSPNode.CreateOwned(mo.FaceGroups);
- // import all faces into leaves & subnodes
- for i := 0 to bsp.NumOfLeaves - 1 do
- begin
- lastfg := nil;
- for j := 0 to bsp.Leaves[i].NumFaces - 1 do
- begin
- n := bsp.Leaves[i].FirstFace + j;
- if n >= bsp.NumOfFaces then
- Break; // corrupted BSP?
- facePtr := @bsp.Faces[n];
- if facePtr.FaceType = FACE_POLYGON then
- begin
- if lastfg = nil then
- fg := TFGBSPNode(mo.FaceGroups[i + bsp.NumOfNodes])
- else
- begin
- lastfg.PositiveSubNodeIndex := mo.FaceGroups.Count;
- fg := TFGBSPNode.CreateOwned(mo.FaceGroups);
- end;
- // check for BSP corruption
- if Cardinal(facePtr.textureID) <= Cardinal(bsp.NumOfTextures) then
- fg.MaterialName := Trim(string(StrPas(bsp.Textures[facePtr.textureID].TextureName)));
- if Assigned(lightmapLib) and vFileQ3BSPLoadMaterials then
- fg.LightMapIndex := facePtr.lightmapID;
- lastfg := fg;
- // Q3 Polygon Faces are actually fans, but winded the other way around!
- fg.Mode := fgmmTriangleFan;
- fg.VertexIndices.Add(facePtr.startVertIndex);
- fg.VertexIndices.AddSerie(facePtr.startVertIndex + facePtr.numOfVerts - 1,
- -1,
- facePtr.numOfVerts - 1);
- // there are also per-leaf mesh references... dunno what they
- // are for, did not encounter them so far... If you have a BSP
- // that has some, and if you know how to make use of them, shout!
- // Copy the cluster index, used for visibility determination
- fg.Cluster := bsp.Leaves[i].Cluster;
- end;
- end;
- end;
- // Copy the visibility data
- if bsp.VisData.numOfClusters > 0 then
- mo.ClusterVisibility.SetData(
- @bsp.VisData.bitSets[0], bsp.VisData.numOfClusters);
- finally
- bsp.Free;
- end;
- // Some BSP end up with empty nodes/leaves (information unused, incorrept BSP...)
- // This call takes care of cleaning up all the empty nodes
- mo.CleanupUnusedNodes;
- end;
- // ------------------------------------------------------------------
- initialization
- // ------------------------------------------------------------------
- RegisterVectorFileFormat('q3bsp', 'Quake3 BSP files', TgxQ3BSPVectorFile);
- // registering this extension too might be a little abusive right now...
- RegisterVectorFileFormat('bsp', 'BSP files', TgxQ3BSPVectorFile);
- end.
|