GXS.FileQ3BSP.pas 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. //
  2. // The graphics engine GXScene https://github.com/glscene
  3. //
  4. unit GXS.FileQ3BSP;
  5. (*
  6. Support-code to load Q3BSP Files into TgxFreeForm-Components.
  7. Note that you must manually add this unit to one of your project's uses
  8. to enable support for OBJ & OBJF at run-time.
  9. *)
  10. interface
  11. {$I Stage.Defines.inc}
  12. uses
  13. System.Classes,
  14. System.SysUtils,
  15. FMX.Graphics,
  16. GXS.VectorFileObjects,
  17. GXS.ApplicationFileIO,
  18. Stage.VectorGeometry,
  19. Stage.VectorTypes,
  20. GXS.VectorLists,
  21. GXS.BSP,
  22. GXS.Texture,
  23. GXS.Graphics,
  24. GXS.State,
  25. GXS.ImageUtils,
  26. GXS.Material,
  27. Stage.TextureFormat,
  28. Formatx.Q3BSP;
  29. type
  30. // The Q3BSP vector file (Quake III BSP).
  31. TgxQ3BSPVectorFile = class(TgxVectorFile)
  32. public
  33. class function Capabilities: TDataFileCapabilities; override;
  34. procedure LoadFromStream(aStream: TStream); override;
  35. end;
  36. var
  37. // Q3 lightmaps are quite dark, we brighten them a lot by default
  38. vQ3BSPLightmapGammaCorrection: Single = 2.5;
  39. vQ3BSPLightmapBrightness: Single = 2; // scaling factor, 1.0 = unchanged
  40. vFileQ3BSPLoadMaterials: boolean = True; // Mrqzzz : Flag to avoid loading materials (useful for IDE Extentions like GlaredX)
  41. // ------------------------------------------------------------------
  42. implementation
  43. // ------------------------------------------------------------------
  44. // ------------------
  45. // ------------------ TGXSTLVectorFile ------------------
  46. // ------------------
  47. class function TgxQ3BSPVectorFile.Capabilities: TDataFileCapabilities;
  48. begin
  49. Result := [dfcRead];
  50. end;
  51. procedure TgxQ3BSPVectorFile.LoadFromStream(aStream: TStream);
  52. function LocateTextureFile(const texName: string): string;
  53. begin
  54. if FileStreamExists(texName + '.bmp') then
  55. Result := texName + '.bmp'
  56. else if FileStreamExists(texName + '.jpg') then
  57. Result := texName + '.jpg'
  58. else if FileStreamExists(texName + '.tga') then
  59. Result := texName + '.tga'
  60. else
  61. Result := '';
  62. end;
  63. function GetOrAllocateMaterial(const matName: string): string;
  64. var
  65. matLib: TgxMaterialLibrary;
  66. libMat: TgxLibMaterial;
  67. texName: string;
  68. begin
  69. if GetOwner is TgxBaseMesh then
  70. begin
  71. // got a linked material library?
  72. matLib := TgxBaseMesh(GetOwner).MaterialLibrary;
  73. if Assigned(matLib) then
  74. begin
  75. Result := matName;
  76. libMat := matLib.Materials.GetLibMaterialByName(matName);
  77. if not Assigned(libMat) then
  78. begin
  79. if Pos('.', matName) < 1 then
  80. begin
  81. texName := LocateTextureFile(matName);
  82. if texName = '' then
  83. texName := LocateTextureFile(Copy(matName, LastDelimiter('\/', matName) + 1, MaxInt));
  84. end
  85. else
  86. texName := matName;
  87. with matLib.AddTextureMaterial(matName, texName) do
  88. begin
  89. Material.Texture.TextureMode := tmModulate;
  90. end;
  91. end;
  92. end
  93. else
  94. Result := '';
  95. end
  96. else
  97. Result := '';
  98. end;
  99. var
  100. bsp: TQ3BSP;
  101. mo: TBSPMeshObject;
  102. fg, lastfg: TFGBSPNode;
  103. i, j, n, y: Integer;
  104. facePtr: PBSPFace;
  105. lightmapLib: TgxMaterialLibrary;
  106. lightmapBmp: TBitmap;
  107. libMat: TgxLibMaterial;
  108. bspLightMap: PBSPLightmap;
  109. plane: THmgPlane;
  110. begin
  111. bsp := TQ3BSP.Create(aStream);
  112. try
  113. mo := TBSPMeshObject.CreateOwned(Owner.MeshObjects);
  114. // import all materials
  115. if vFileQ3BSPLoadMaterials then
  116. begin
  117. for i := 0 to High(bsp.Textures) do
  118. begin
  119. GetOrAllocateMaterial(Trim(string(StrPas(bsp.Textures[i].TextureName))));
  120. end;
  121. end;
  122. // import all lightmaps
  123. lightmapLib := Owner.LightmapLibrary;
  124. if Assigned(lightmapLib) and vFileQ3BSPLoadMaterials then
  125. begin
  126. // import lightmaps
  127. n := bsp.NumOfLightmaps;
  128. lightmapBmp := TBitmap.Create;
  129. try
  130. { TODO : E2129 Cannot assign to a read-only property }
  131. (*lightmapBmp.PixelFormat := glpf24bit;*)
  132. lightmapBmp.Width := 128;
  133. lightmapBmp.Height := 128;
  134. for i := 0 to n - 1 do
  135. begin
  136. bspLightMap := @bsp.Lightmaps[i];
  137. // apply brightness correction if ant
  138. if vQ3BSPLightmapBrightness <> 1 then
  139. BrightenRGBArray(@bspLightMap.imageBits[0], 128 * 128,
  140. vQ3BSPLightmapBrightness);
  141. // apply gamma correction if any
  142. if vQ3BSPLightmapGammaCorrection <> 1 then
  143. GammaCorrectRGBArray(@bspLightMap.imageBits[0], 128 * 128,
  144. vQ3BSPLightmapGammaCorrection);
  145. // convert RAW RGB to BMP
  146. for y := 0 to 127 do
  147. { TODO : E2003 Undeclared identifier: 'ScanLine' }
  148. (*
  149. BGR24ToRGB24(@bspLightMap.imageBits[y * 128 * 3],
  150. lightmapBmp.ScanLine[127 - y], 128);
  151. *)
  152. // spawn lightmap
  153. libMat := lightmapLib.AddTextureMaterial(IntToStr(i), lightmapBmp);
  154. with libMat.Material.Texture do
  155. begin
  156. MinFilter := miLinear;
  157. TextureWrap := twNone;
  158. TextureFormat := tfRGB;
  159. end;
  160. end;
  161. finally
  162. lightmapBmp.Free;
  163. end;
  164. end;
  165. // import all geometry
  166. mo.Vertices.AdjustCapacityToAtLeast(bsp.NumOfVerts);
  167. mo.Normals.AdjustCapacityToAtLeast(bsp.NumOfVerts);
  168. mo.TexCoords.AdjustCapacityToAtLeast(bsp.NumOfVerts);
  169. for i := 0 to bsp.NumOfVerts - 1 do
  170. begin
  171. mo.Vertices.Add(bsp.Vertices[i].Position);
  172. mo.Normals.Add(bsp.Vertices[i].Normal);
  173. mo.TexCoords.Add(bsp.Vertices[i].TextureCoord);
  174. if Assigned(lightMapLib) and vFileQ3BSPLoadMaterials then
  175. mo.LightMapTexCoords.Add(bsp.Vertices[i].LightmapCoord)
  176. end;
  177. mo.TexCoords.Scale(AffineVectorMake(1, -1, 0));
  178. mo.TexCoords.Translate(YVector);
  179. mo.RenderSort := rsBackToFront;
  180. // Q3 BSP separates tree nodes from leafy nodes, we don't,
  181. // so we place nodes first, then all leafs afterwards
  182. for i := 0 to bsp.NumOfNodes - 1 do
  183. begin
  184. fg := TFGBSPNode.CreateOwned(mo.FaceGroups);
  185. plane := bsp.Planes[bsp.Nodes[i].plane];
  186. plane := VectorMake(plane.X, plane.Y, plane.Z, plane.W);
  187. fg.SplitPlane := plane;
  188. fg.PositiveSubNodeIndex := bsp.Nodes[i].Children[0];
  189. if fg.PositiveSubNodeIndex < 0 then
  190. fg.PositiveSubNodeIndex := bsp.NumOfNodes - fg.PositiveSubNodeIndex - 1;
  191. Assert(fg.PositiveSubNodeIndex < bsp.NumOfNodes + bsp.NumOfLeaves);
  192. Assert(fg.PositiveSubNodeIndex > 0);
  193. fg.NegativeSubNodeIndex := bsp.Nodes[i].Children[1];
  194. if fg.NegativeSubNodeIndex < 0 then
  195. fg.NegativeSubNodeIndex := bsp.NumOfNodes - fg.NegativeSubNodeIndex - 1;
  196. Assert(fg.NegativeSubNodeIndex < bsp.NumOfNodes + bsp.NumOfLeaves);
  197. Assert(fg.NegativeSubNodeIndex > 0);
  198. end;
  199. // import all leaves
  200. for i := 0 to bsp.NumOfLeaves - 1 do
  201. TFGBSPNode.CreateOwned(mo.FaceGroups);
  202. // import all faces into leaves & subnodes
  203. for i := 0 to bsp.NumOfLeaves - 1 do
  204. begin
  205. lastfg := nil;
  206. for j := 0 to bsp.Leaves[i].NumFaces - 1 do
  207. begin
  208. n := bsp.Leaves[i].FirstFace + j;
  209. if n >= bsp.NumOfFaces then
  210. Break; // corrupted BSP?
  211. facePtr := @bsp.Faces[n];
  212. if facePtr.FaceType = FACE_POLYGON then
  213. begin
  214. if lastfg = nil then
  215. fg := TFGBSPNode(mo.FaceGroups[i + bsp.NumOfNodes])
  216. else
  217. begin
  218. lastfg.PositiveSubNodeIndex := mo.FaceGroups.Count;
  219. fg := TFGBSPNode.CreateOwned(mo.FaceGroups);
  220. end;
  221. // check for BSP corruption
  222. if Cardinal(facePtr.textureID) <= Cardinal(bsp.NumOfTextures) then
  223. fg.MaterialName := Trim(string(StrPas(bsp.Textures[facePtr.textureID].TextureName)));
  224. if Assigned(lightmapLib) and vFileQ3BSPLoadMaterials then
  225. fg.LightMapIndex := facePtr.lightmapID;
  226. lastfg := fg;
  227. // Q3 Polygon Faces are actually fans, but winded the other way around!
  228. fg.Mode := fgmmTriangleFan;
  229. fg.VertexIndices.Add(facePtr.startVertIndex);
  230. fg.VertexIndices.AddSerie(facePtr.startVertIndex + facePtr.numOfVerts - 1,
  231. -1,
  232. facePtr.numOfVerts - 1);
  233. // there are also per-leaf mesh references... dunno what they
  234. // are for, did not encounter them so far... If you have a BSP
  235. // that has some, and if you know how to make use of them, shout!
  236. // Copy the cluster index, used for visibility determination
  237. fg.Cluster := bsp.Leaves[i].Cluster;
  238. end;
  239. end;
  240. end;
  241. // Copy the visibility data
  242. if bsp.VisData.numOfClusters > 0 then
  243. mo.ClusterVisibility.SetData(
  244. @bsp.VisData.bitSets[0], bsp.VisData.numOfClusters);
  245. finally
  246. bsp.Free;
  247. end;
  248. // Some BSP end up with empty nodes/leaves (information unused, incorrept BSP...)
  249. // This call takes care of cleaning up all the empty nodes
  250. mo.CleanupUnusedNodes;
  251. end;
  252. // ------------------------------------------------------------------
  253. initialization
  254. // ------------------------------------------------------------------
  255. RegisterVectorFileFormat('q3bsp', 'Quake3 BSP files', TgxQ3BSPVectorFile);
  256. // registering this extension too might be a little abusive right now...
  257. RegisterVectorFileFormat('bsp', 'BSP files', TgxQ3BSPVectorFile);
  258. end.