GLS.FileQ3BSP.pas 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. //
  2. // The graphics engine GLXEngine. The unit of GLScene for Delphi
  3. //
  4. unit GLS.FileQ3BSP;
  5. (*
  6. Support-code to load Q3BSP Files into TGLFreeForm-Components in GLScene.
  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. Vcl.Graphics,
  16. GLS.VectorFileObjects,
  17. GLS.ApplicationFileIO,
  18. Stage.VectorGeometry,
  19. Stage.VectorTypes,
  20. GLS.VectorLists,
  21. Formats.Q3BSP,
  22. GLS.MeshBSP,
  23. GLS.Texture,
  24. GLS.Graphics,
  25. GLS.State,
  26. Stage.Utils,
  27. GLS.Material,
  28. Stage.TextureFormat;
  29. type
  30. //The Q3BSP vector file (Quake III BSP).
  31. TGLQ3BSPVectorFile = class(TGLVectorFile)
  32. public
  33. class function Capabilities: TGLDataFileCapabilities; 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. vGLFileQ3BSPLoadMaterials: boolean = True; // Flag to avoid loading materials (useful for IDE Extentions)
  41. // ------------------------------------------------------------------
  42. implementation
  43. // ------------------------------------------------------------------
  44. // ------------------
  45. // ------------------ TGLSTLVectorFile ------------------
  46. // ------------------
  47. class function TGLQ3BSPVectorFile.Capabilities: TGLDataFileCapabilities;
  48. begin
  49. Result := [dfcRead];
  50. end;
  51. procedure TGLQ3BSPVectorFile.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: TGLMaterialLibrary;
  66. libMat: TGLLibMaterial;
  67. texName: string;
  68. begin
  69. if GetOwner is TGLBaseMesh then
  70. begin
  71. // got a linked material library?
  72. matLib := TGLBaseMesh(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: TGLMaterialLibrary;
  106. lightmapBmp: TBitmap;
  107. libMat: TGLLibMaterial;
  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 vGLFileQ3BSPLoadMaterials 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 vGLFileQ3BSPLoadMaterials then
  125. begin
  126. // import lightmaps
  127. n := bsp.NumOfLightmaps;
  128. lightmapBmp := TBitmap.Create;
  129. try
  130. lightmapBmp.PixelFormat := pf24bit;
  131. lightmapBmp.Width := 128;
  132. lightmapBmp.Height := 128;
  133. for i := 0 to n - 1 do
  134. begin
  135. bspLightMap := @bsp.Lightmaps[i];
  136. // apply brightness correction if ant
  137. if vQ3BSPLightmapBrightness <> 1 then
  138. BrightenRGBArray(@bspLightMap.imageBits[0], 128 * 128,
  139. vQ3BSPLightmapBrightness);
  140. // apply gamma correction if any
  141. if vQ3BSPLightmapGammaCorrection <> 1 then
  142. GammaCorrectRGBArray(@bspLightMap.imageBits[0], 128 * 128,
  143. vQ3BSPLightmapGammaCorrection);
  144. // convert RAW RGB to BMP
  145. for y := 0 to 127 do
  146. BGR24ToRGB24(@bspLightMap.imageBits[y * 128 * 3],
  147. lightmapBmp.ScanLine[127 - y], 128);
  148. // spawn lightmap
  149. libMat := lightmapLib.AddTextureMaterial(IntToStr(i), lightmapBmp);
  150. with libMat.Material.Texture do
  151. begin
  152. MinFilter := miLinear;
  153. TextureWrap := twNone;
  154. TextureFormat := tfRGB;
  155. end;
  156. end;
  157. finally
  158. lightmapBmp.Free;
  159. end;
  160. end;
  161. // import all geometry
  162. mo.Vertices.AdjustCapacityToAtLeast(bsp.NumOfVerts);
  163. mo.Normals.AdjustCapacityToAtLeast(bsp.NumOfVerts);
  164. mo.TexCoords.AdjustCapacityToAtLeast(bsp.NumOfVerts);
  165. for i := 0 to bsp.NumOfVerts - 1 do
  166. begin
  167. mo.Vertices.Add(bsp.Vertices[i].Position);
  168. mo.Normals.Add(bsp.Vertices[i].Normal);
  169. mo.TexCoords.Add(bsp.Vertices[i].TextureCoord);
  170. if Assigned(lightMapLib) and vGLFileQ3BSPLoadMaterials then
  171. mo.LightMapTexCoords.Add(bsp.Vertices[i].LightmapCoord)
  172. end;
  173. mo.TexCoords.Scale(AffineVectorMake(1, -1, 0));
  174. mo.TexCoords.Translate(YVector);
  175. mo.RenderSort := rsBackToFront;
  176. // Q3 BSP separates tree nodes from leafy nodes, we don't,
  177. // so we place nodes first, then all leafs afterwards
  178. for i := 0 to bsp.NumOfNodes - 1 do
  179. begin
  180. fg := TFGBSPNode.CreateOwned(mo.FaceGroups);
  181. plane := bsp.Planes[bsp.Nodes[i].plane];
  182. plane := VectorMake(plane.X, plane.Y, plane.Z, plane.W);
  183. fg.SplitPlane := plane;
  184. fg.PositiveSubNodeIndex := bsp.Nodes[i].Children[0];
  185. if fg.PositiveSubNodeIndex < 0 then
  186. fg.PositiveSubNodeIndex := bsp.NumOfNodes - fg.PositiveSubNodeIndex - 1;
  187. Assert(fg.PositiveSubNodeIndex < bsp.NumOfNodes + bsp.NumOfLeaves);
  188. Assert(fg.PositiveSubNodeIndex > 0);
  189. fg.NegativeSubNodeIndex := bsp.Nodes[i].Children[1];
  190. if fg.NegativeSubNodeIndex < 0 then
  191. fg.NegativeSubNodeIndex := bsp.NumOfNodes - fg.NegativeSubNodeIndex - 1;
  192. Assert(fg.NegativeSubNodeIndex < bsp.NumOfNodes + bsp.NumOfLeaves);
  193. Assert(fg.NegativeSubNodeIndex > 0);
  194. end;
  195. // import all leaves
  196. for i := 0 to bsp.NumOfLeaves - 1 do
  197. TFGBSPNode.CreateOwned(mo.FaceGroups);
  198. // import all faces into leaves & subnodes
  199. for i := 0 to bsp.NumOfLeaves - 1 do
  200. begin
  201. lastfg := nil;
  202. for j := 0 to bsp.Leaves[i].NumFaces - 1 do
  203. begin
  204. n := bsp.Leaves[i].FirstFace + j;
  205. if n >= bsp.NumOfFaces then
  206. Break; // corrupted BSP?
  207. facePtr := @bsp.Faces[n];
  208. if facePtr.FaceType = FACE_POLYGON then
  209. begin
  210. if lastfg = nil then
  211. fg := TFGBSPNode(mo.FaceGroups[i + bsp.NumOfNodes])
  212. else
  213. begin
  214. lastfg.PositiveSubNodeIndex := mo.FaceGroups.Count;
  215. fg := TFGBSPNode.CreateOwned(mo.FaceGroups);
  216. end;
  217. // check for BSP corruption
  218. if Cardinal(facePtr.textureID) <= Cardinal(bsp.NumOfTextures) then
  219. fg.MaterialName := Trim(string(StrPas(bsp.Textures[facePtr.textureID].TextureName)));
  220. if Assigned(lightmapLib) and vGLFileQ3BSPLoadMaterials then
  221. fg.LightMapIndex := facePtr.lightmapID;
  222. lastfg := fg;
  223. // Q3 Polygon Faces are actually fans, but winded the other way around!
  224. fg.Mode := fgmmTriangleFan;
  225. fg.VertexIndices.Add(facePtr.startVertIndex);
  226. fg.VertexIndices.AddSerie(facePtr.startVertIndex + facePtr.numOfVerts - 1,
  227. -1,
  228. facePtr.numOfVerts - 1);
  229. // there are also per-leaf mesh references... dunno what they
  230. // are for, did not encounter them so far... If you have a BSP
  231. // that has some, and if you know how to make use of them, shout!
  232. // Copy the cluster index, used for visibility determination
  233. fg.Cluster := bsp.Leaves[i].Cluster;
  234. end;
  235. end;
  236. end;
  237. // Copy the visibility data
  238. if bsp.VisData.numOfClusters > 0 then
  239. mo.ClusterVisibility.SetData(
  240. @bsp.VisData.bitSets[0], bsp.VisData.numOfClusters);
  241. finally
  242. bsp.Free;
  243. end;
  244. // Some BSP end up with empty nodes/leaves (information unused, incorrept BSP...)
  245. // This call takes care of cleaning up all the empty nodes
  246. mo.CleanupUnusedNodes;
  247. end;
  248. // ------------------------------------------------------------------
  249. initialization
  250. // ------------------------------------------------------------------
  251. RegisterVectorFileFormat('q3bsp', 'Quake3 BSP files', TGLQ3BSPVectorFile);
  252. // registering this extension too might be a little abusive right now...
  253. RegisterVectorFileFormat('bsp', 'BSP files', TGLQ3BSPVectorFile);
  254. end.