├── FUTURE PROBLEM.txt ├── LICENSES ├── RADIANCE_License.txt ├── VALVE_LICENSE.txt └── thirdpartylegalnotices.txt ├── Modules ├── SourceBSP │ ├── UnitBSPstruct.pas │ ├── UnitDispMisc.pas │ └── UnitEntity.pas ├── UnitBufferObjectManager.pas ├── UnitImageTGA.pas ├── UnitMegatextureManager.pas ├── UnitOpenGLErrorManager.pas ├── UnitOpenGLFPSCamera.pas ├── UnitOpenGLext.pas ├── UnitQueryPerformanceProfiler.pas ├── UnitQueryPerformanceTimer.pas ├── UnitRadianceHDR.pas ├── UnitRenderTimerManager.pas ├── UnitRenderingContextManager.pas ├── UnitRescaling2D.pas ├── UnitShaderManager.pas ├── UnitUserTypes.pas ├── UnitVTFMeowLib.pas ├── UnitVec.pas └── UnitVertexBufferArrayManager.pas ├── README.md ├── SourceBSPViewer.cfg ├── SourceBSPViewer.dof ├── SourceBSPViewer.dpr ├── SourceBSPViewer.exe ├── SourceBSPViewer.res ├── SourceEngineMapViewer.exe ├── Unit1.ddp ├── Unit1.dfm ├── Unit1.pas ├── changes.txt ├── shaders ├── CubeFRAG.cpp ├── CubeVERT.cpp ├── DispFRAG.cpp ├── DispVERT.cpp ├── FaceFRAG.cpp ├── FaceVERT.cpp ├── OrtsFRAG.cpp ├── OrtsVERT.cpp ├── SFaceFRAG.cpp └── SFaceVERT.cpp └── title.png /FUTURE PROBLEM.txt: -------------------------------------------------------------------------------- 1 | for add resize ligthmap function - need add function for resize BSP map - make re-save each lump. 2 | lump 35 or "Game lump" contain offsets to raw data relative begining of BSP file. 3 | If need resize BSP -> need shift lumps -> need recalculate offsets to raw data in lump 35. 4 | ### Also need share source code if exists other lumps that hame same problem as lump 35. - checked, only lump 35 have this problem. -------------------------------------------------------------------------------- /LICENSES/RADIANCE_License.txt: -------------------------------------------------------------------------------- 1 | The Radiance Software License, Version 1.0 2 | 3 | Copyright (c) 1990 - 2018 The Regents of the University of California, 4 | through Lawrence Berkeley National Laboratory. All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions 8 | are met: 9 | 10 | 1. Redistributions of source code must retain the above copyright 11 | notice, this list of conditions and the following disclaimer. 12 | 13 | 2. Redistributions in binary form must reproduce the above copyright 14 | notice, this list of conditions and the following disclaimer in 15 | the documentation and/or other materials provided with the 16 | distribution. 17 | 18 | 3. The end-user documentation included with the redistribution, 19 | if any, must include the following acknowledgment: 20 | "This product includes Radiance software 21 | (http://radsite.lbl.gov/) 22 | developed by the Lawrence Berkeley National Laboratory 23 | (http://www.lbl.gov/)." 24 | Alternately, this acknowledgment may appear in the software itself, 25 | if and wherever such third-party acknowledgments normally appear. 26 | 27 | 4. The names "Radiance," "Lawrence Berkeley National Laboratory" 28 | and "The Regents of the University of California" must 29 | not be used to endorse or promote products derived from this 30 | software without prior written permission. For written 31 | permission, please contact radiance@radsite.lbl.gov. 32 | 33 | 5. Products derived from this software may not be called "Radiance", 34 | nor may "Radiance" appear in their name, without prior written 35 | permission of Lawrence Berkeley National Laboratory. 36 | 37 | THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 38 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 39 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 40 | DISCLAIMED. IN NO EVENT SHALL Lawrence Berkeley National Laboratory OR 41 | ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 42 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 43 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 44 | USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 45 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 46 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 47 | OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 48 | SUCH DAMAGE. 49 | ==================================================================== 50 | 51 | This software consists of voluntary contributions made by many 52 | individuals on behalf of Lawrence Berkeley National Laboratory. For more 53 | information on Lawrence Berkeley National Laboratory, please see 54 | . 55 | -------------------------------------------------------------------------------- /LICENSES/VALVE_LICENSE.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sergey-KoRJiK/SourceEngineBSPditor/5be52ab21baeea56188fb688e73041e4b2fa2ebd/LICENSES/VALVE_LICENSE.txt -------------------------------------------------------------------------------- /Modules/SourceBSP/UnitDispMisc.pas: -------------------------------------------------------------------------------- 1 | unit UnitDispMisc; 2 | 3 | interface 4 | 5 | uses 6 | OpenGL, 7 | UnitUserTypes; 8 | 9 | const 10 | DISP_SIZE_BY_POWER_2 = 5; // 2^2 + 1 11 | DISP_SIZE_BY_POWER_3 = 9; // 2^3 + 1 12 | DISP_SIZE_BY_POWER_4 = 17; // 2^4 + 1 13 | 14 | // Disp Triangle Count = Sqr(Size - 1)*2; 15 | // Power = 2..4 -> Size = 5, 9, 17 16 | // -> Count = 32, 128, 512; 17 | // Disp Vertex Triangle Count = Count*3 -> 96, 384, 1536 18 | DISP_TRIG_INDEX_COUNT_POWER_2 = 96; 19 | DISP_TRIG_INDEX_COUNT_POWER_3 = 384; 20 | DISP_TRIG_INDEX_COUNT_POWER_4 = 1536; 21 | 22 | var 23 | DISP_TRIG_INDEX_TABLE_VALID: Boolean = False; 24 | DISP_TRIG_INDEX_TABLE_POWER_2: array[0..DISP_TRIG_INDEX_COUNT_POWER_2-1] of GLuint; 25 | DISP_TRIG_INDEX_TABLE_POWER_3: array[0..DISP_TRIG_INDEX_COUNT_POWER_3-1] of GLuint; 26 | DISP_TRIG_INDEX_TABLE_POWER_4: array[0..DISP_TRIG_INDEX_COUNT_POWER_4-1] of GLuint; 27 | 28 | 29 | procedure InitDispTriangleIndexTable(); 30 | 31 | // Tessellating point "p" in flat 4-polygon "poly" by fractions "f" 32 | // f.xy must be [0..1], poly must be a list of four continious polygon vertices 33 | // Formula: 34 | // tmpA = (1 - f.x)*poly[0] + f.x*poly[1]; 35 | // tmpB = (1 - f.x)*poly[3] + f.x*poly[2]; 36 | // p = (1 - f.y)*tmpA + f.y*tmpB; 37 | procedure TessellatingFlatDispPoint(const f: PVec2f; const poly, p: PVec3f); 38 | 39 | 40 | implementation 41 | 42 | 43 | procedure InitDispTriangleIndexTable(); 44 | var 45 | i, j, TrigIndex, PowerIndex: Integer; 46 | OffsetTrigIndex, VertOffset, VertOffsetNext: Integer; 47 | DispSizes: array[0..2] of Integer; 48 | DispTables: array[0..2] of Pointer; 49 | begin 50 | {$R-} 51 | if (DISP_TRIG_INDEX_TABLE_VALID) then Exit; 52 | 53 | DispSizes[0]:=DISP_SIZE_BY_POWER_2; 54 | DispSizes[1]:=DISP_SIZE_BY_POWER_3; 55 | DispSizes[2]:=DISP_SIZE_BY_POWER_4; 56 | 57 | DispTables[0]:=@DISP_TRIG_INDEX_TABLE_POWER_2[0]; 58 | DispTables[1]:=@DISP_TRIG_INDEX_TABLE_POWER_3[0]; 59 | DispTables[2]:=@DISP_TRIG_INDEX_TABLE_POWER_4[0]; 60 | 61 | // Get Table 62 | for PowerIndex:=0 to 2 do 63 | begin 64 | for i:=0 to (DispSizes[PowerIndex] - 2) do 65 | begin 66 | OffsetTrigIndex:=i*(DispSizes[PowerIndex] - 1)*2; 67 | VertOffset:=i*DispSizes[PowerIndex]; 68 | VertOffsetNext:=VertOffset + DispSizes[PowerIndex]; 69 | 70 | for j:=0 to (DispSizes[PowerIndex] - 2) do 71 | begin 72 | // Upper Triangle 73 | TrigIndex:=2*j + OffsetTrigIndex; 74 | AInt(DispTables[PowerIndex])[3*TrigIndex]:= VertOffsetNext + j; 75 | AInt(DispTables[PowerIndex])[3*TrigIndex + 1]:= VertOffset + j + 1; 76 | AInt(DispTables[PowerIndex])[3*TrigIndex + 2]:= VertOffset + j; 77 | 78 | // Downer Triangle 79 | Inc(TrigIndex); 80 | AInt(DispTables[PowerIndex])[3*TrigIndex]:= VertOffsetNext + j; 81 | AInt(DispTables[PowerIndex])[3*TrigIndex + 1]:= VertOffsetNext + j + 1; 82 | AInt(DispTables[PowerIndex])[3*TrigIndex + 2]:= VertOffset + j + 1; 83 | end; 84 | end; 85 | end; 86 | 87 | DISP_TRIG_INDEX_TABLE_VALID:=True; 88 | {$R+} 89 | end; 90 | 91 | 92 | procedure TessellatingFlatDispPoint(const f: PVec2f; const poly, p: PVec3f); 93 | var 94 | tmpA, tmpB: tVec3f; 95 | begin 96 | {$R-} 97 | if (f = nil) or (poly = nil) or (p = nil) then Exit; 98 | 99 | tmpA.x:=(1 - f.x)*AVec3f(poly)[0].x + f.x*AVec3f(poly)[1].x; 100 | tmpA.y:=(1 - f.x)*AVec3f(poly)[0].y + f.x*AVec3f(poly)[1].y; 101 | tmpA.z:=(1 - f.x)*AVec3f(poly)[0].z + f.x*AVec3f(poly)[1].z; 102 | 103 | tmpB.x:=(1 - f.x)*AVec3f(poly)[3].x + f.x*AVec3f(poly)[2].x; 104 | tmpB.y:=(1 - f.x)*AVec3f(poly)[3].y + f.x*AVec3f(poly)[2].y; 105 | tmpB.z:=(1 - f.x)*AVec3f(poly)[3].z + f.x*AVec3f(poly)[2].z; 106 | 107 | p.x:=(1 - f.y)*tmpA.x + f.y*tmpB.x; 108 | p.y:=(1 - f.y)*tmpA.y + f.y*tmpB.y; 109 | p.z:=(1 - f.y)*tmpA.z + f.y*tmpB.z; 110 | {$R+} 111 | end; 112 | 113 | end. 114 | -------------------------------------------------------------------------------- /Modules/SourceBSP/UnitEntity.pas: -------------------------------------------------------------------------------- 1 | unit UnitEntity; 2 | 3 | // Copyright (c) 2020 Sergey-KoRJiK, Belarus 4 | // github.com/Sergey-KoRJiK 5 | 6 | interface 7 | 8 | uses 9 | SysUtils, 10 | Windows, 11 | Classes, 12 | UnitUserTypes, 13 | UnitVec; 14 | 15 | const 16 | MAX_KEY_SIZE = 32; 17 | MAX_VALUE_SIZE = 1024; 18 | 19 | type tKeyValue = packed record 20 | Key : String; 21 | Value : String; 22 | end; 23 | type PKeyValue = ^tKeyValue; 24 | type AKeyValue = array of tKeyValue; 25 | 26 | type tEntity = packed record 27 | CountPairs: Integer; 28 | Pairs : AKeyValue; 29 | ClassName : String; 30 | TargetName: String; 31 | Origin : tVec3f; 32 | Angles : tVec3f; 33 | isOrigin : ByteBool; 34 | isAngles : ByteBool; 35 | VisLeaf : SmallInt; 36 | BModel : Integer; // -1 for point entities, 0 - worldspawn, 1.. - BModels 37 | end; 38 | type PEntity = ^tEntity; 39 | type AEntity = array of tEntity; 40 | 41 | 42 | const 43 | KeyOrigin = 'origin'; 44 | KeyAngles = 'angles'; 45 | KeyTargetName = 'targetname'; 46 | KeyClassName = 'classname'; 47 | KeyBModel = 'model'; 48 | KeyWorldspawn = 'worldspawn'; 49 | // 50 | KeyLightStyle = 'style'; 51 | ClassNameSpawnSP = 'info_player_start'; 52 | ClassNameSpawnDM = 'info_player_deathmatch'; 53 | ClassNameSpawnCT = 'info_player_counterterrorist'; 54 | ClassNameSpawnT = 'info_player_terrorist'; 55 | ClassNameLight = 'light'; 56 | ClassNameLightSpot = 'light_spot'; 57 | ClassNameLightEnv = 'light_environment'; 58 | 59 | 60 | function GetEntityList(const data: PByte; const dataSz: Integer): AEntity; 61 | procedure FreeEntity(const Entity: PEntity); 62 | 63 | function GetPairIndexByKey(const Pairs: PKeyValue; const CountPairs: Integer; 64 | const Key: String): Integer; 65 | function GetPairIndexByValue(const Pairs: PKeyValue; const CountPairs: Integer; 66 | const Value: String): Integer; 67 | function GetFirstPairIndexBySubValue(const Pairs: PKeyValue; const CountPairs: Integer; 68 | const SubValue: String): Integer; 69 | 70 | function FindFirstSpawnEntity(const Entities: PEntity; const Count: Integer): Integer; 71 | function FindEntityByClassName(const Entities: PEntity; const Count: Integer; 72 | const ClassNameKey: String): Integer; 73 | function FindEntityByTargetName(const Entities: PEntity; const Count: Integer; 74 | const TargetNameKey: String): Integer; 75 | function FindEntityByBModelIndex(const Entities: PEntity; const Count: Integer; 76 | const BModelIndex: Integer): Integer; 77 | 78 | 79 | implementation 80 | 81 | 82 | procedure ParseEntityPair(const StrPair: String; const Pair: PKeyValue); 83 | var 84 | i, j, n: Integer; 85 | QuotesPos: array[0..3] of Integer; 86 | begin 87 | {$R-} 88 | n:=Length(StrPair); 89 | 90 | j:=0; 91 | for i:=1 to n do 92 | begin 93 | if (StrPair[i] = '"') then Inc(j); 94 | end; 95 | 96 | if (j <> 4) then 97 | begin 98 | Pair.Key:=''; 99 | Pair.Value:=''; 100 | end 101 | else 102 | begin 103 | j:=0; 104 | for i:=1 to n do 105 | begin 106 | if (StrPair[i] = '"') then 107 | begin 108 | QuotesPos[j]:=i; 109 | Inc(j); 110 | end; 111 | end; 112 | 113 | if ((QuotesPos[1] - QuotesPos[0] - 1) > MAX_KEY_SIZE) then 114 | begin 115 | Pair.Key:=Copy(StrPair, QuotesPos[0] + 1, MAX_KEY_SIZE); 116 | end 117 | else 118 | begin 119 | Pair.Key:=Copy(StrPair, QuotesPos[0] + 1, QuotesPos[1] - QuotesPos[0] - 1); 120 | end; 121 | 122 | if ((QuotesPos[3] - QuotesPos[2] - 1) > MAX_VALUE_SIZE) then 123 | begin 124 | Pair.Value:=Copy(StrPair, QuotesPos[2] + 1, MAX_VALUE_SIZE); 125 | end 126 | else 127 | begin 128 | Pair.Value:=Copy(StrPair, QuotesPos[2] + 1, QuotesPos[3] - QuotesPos[2] - 1); 129 | end; 130 | end; 131 | {$R+} 132 | end; 133 | 134 | function GetEntityList(const data: PByte; const dataSz: Integer): AEntity; 135 | var 136 | i, j, k, EntCnt: Integer; 137 | DebugStr, workData: String; 138 | BraCount, KetCount, RowCount: Integer; 139 | BraIndecies, KetIndecies, RowIndex: AInt; 140 | RawList: TStringList; 141 | begin 142 | {$R-} 143 | Result:=nil; 144 | if (data = nil) or (dataSz <= 0) then Exit; 145 | 146 | SetLength(workData, dataSz); 147 | for i:=1 to dataSz do 148 | begin 149 | PByte(@workData[i])^:=AByte(data)[i-1]; 150 | if (workData[i] = #$0D) then workData[i]:=#$20; 151 | end; 152 | workData[dataSz-1]:=#$0A; 153 | 154 | RawList:=TStringList.Create; 155 | RowCount:=0; 156 | for i:=1 to dataSz do 157 | begin 158 | if (workData[i] = #$0A) then Inc(RowCount); 159 | end; 160 | 161 | if (RowCount <= 1) then 162 | begin 163 | RawList.Append(workData); 164 | end 165 | else 166 | begin 167 | SetLength(RowIndex, RowCount); 168 | j:=0; 169 | for i:=1 to dataSz do 170 | begin 171 | if (workData[i] = #$0A) then 172 | begin 173 | RowIndex[j]:=i; 174 | Inc(j); 175 | end; 176 | end; 177 | 178 | DebugStr:=StringReplace( 179 | Copy(workData, 0, RowIndex[0]), #$0A, '', [rfReplaceAll] 180 | ); 181 | RawList.Append(DebugStr); 182 | for i:=0 to (RowCount - 1) do 183 | begin 184 | DebugStr:=StringReplace( 185 | Copy(workData, RowIndex[i], RowIndex[i + 1] - RowIndex[i]), 186 | #$0A, '', [rfReplaceAll]); 187 | RawList.Append(DebugStr); 188 | end; 189 | SetLength(RowIndex, 0); 190 | end; 191 | 192 | /////////////////////////////////////////////////////// 193 | 194 | 195 | BraCount:=0; 196 | KetCount:=0; 197 | for i:=0 to (RawList.Count - 1) do 198 | begin 199 | if (RawList.Strings[i] = '{') then Inc(BraCount); 200 | if (RawList.Strings[i] = '}') then Inc(KetCount); 201 | end; 202 | //if (BraCount <> KetCount) then Exit; 203 | EntCnt:=BraCount; 204 | if (KetCount > BraCount) then EntCnt:=KetCount; 205 | if (EntCnt = 0) then Exit; 206 | 207 | SetLength(Result, EntCnt); 208 | SetLength(BraIndecies, EntCnt); 209 | SetLength(KetIndecies, EntCnt); 210 | j:=0; 211 | k:=0; 212 | for i:=0 to (RawList.Count - 1) do 213 | begin 214 | if (RawList.Strings[i] = '{') then 215 | begin 216 | BraIndecies[j]:=i; 217 | Inc(j); 218 | end; 219 | if (RawList.Strings[i] = '}') then 220 | begin 221 | KetIndecies[k]:=i; 222 | Inc(k); 223 | end; 224 | end; 225 | 226 | for i:=0 to (EntCnt - 1) do 227 | begin 228 | if (BraIndecies[i] > KetIndecies[i]) then 229 | begin 230 | SetLength(Result, 0); 231 | SetLength(BraIndecies, 0); 232 | SetLength(KetIndecies, 0); 233 | Result:=nil; 234 | Exit; 235 | end; 236 | end; 237 | 238 | for i:=0 to (EntCnt - 1) do 239 | begin 240 | Result[i].BModel:=-1; 241 | Result[i].VisLeaf:=-1; 242 | Result[i].CountPairs:=KetIndecies[i] - BraIndecies[i] - 1; 243 | SetLength(Result[i].Pairs, Result[i].CountPairs); 244 | k:=0; 245 | for j:=(BraIndecies[i] + 1) to (KetIndecies[i] - 1) do 246 | begin 247 | ParseEntityPair(RawList.Strings[j], @Result[i].Pairs[k]); 248 | Inc(k); 249 | end; 250 | 251 | Result[i].TargetName:=''; 252 | Result[i].ClassName:=''; 253 | Result[i].Origin:=VEC_ZERO_3F; 254 | Result[i].Angles:=VEC_ZERO_3F; 255 | Result[i].isOrigin:=False; 256 | Result[i].isAngles:=False; 257 | for j:=0 to (Result[i].CountPairs - 1) do 258 | begin 259 | Result[i].Pairs[j].Key:=LowerCase(Result[i].Pairs[j].Key); 260 | if (Result[i].Pairs[j].Key = KeyClassName) then 261 | begin 262 | Result[i].ClassName:=Result[i].Pairs[j].Value; 263 | end; 264 | if (Result[i].Pairs[j].Key = KeyTargetName) then 265 | begin 266 | Result[i].TargetName:=Result[i].Pairs[j].Value; 267 | end; 268 | if (Result[i].Pairs[j].Key = KeyOrigin) then 269 | begin 270 | DebugStr:=Result[i].Pairs[j].Value; 271 | Result[i].isOrigin:=StrToVec(DebugStr, @Result[i].Origin); 272 | end; 273 | if (Result[i].Pairs[j].Key = KeyAngles) then 274 | begin 275 | DebugStr:=Result[i].Pairs[j].Value; 276 | Result[i].isAngles:=StrToVec(DebugStr, @Result[i].Angles); 277 | end; 278 | if (Result[i].Pairs[j].Key = KeyBModel) then 279 | begin 280 | DebugStr:=Result[i].Pairs[j].Value; 281 | Delete(DebugStr, 1, 1); 282 | Result[i].BModel:=StrToIntDef(DebugStr, -1); 283 | end; 284 | end; 285 | if (Result[i].ClassName = KeyWorldspawn) then Result[i].BModel:=0; 286 | end; 287 | 288 | RawList.Destroy; 289 | workData:=''; 290 | DebugStr:=''; 291 | {$R+} 292 | end; 293 | 294 | procedure FreeEntity(const Entity: PEntity); 295 | var 296 | i: Integer; 297 | begin 298 | {$R-} 299 | if (Entity = nil) then Exit; 300 | for i:=0 to Entity.CountPairs-1 do 301 | begin 302 | Entity.Pairs[i].Key:=''; 303 | Entity.Pairs[i].Value:=''; 304 | end; 305 | SetLength(Entity.Pairs, 0); 306 | Entity.Pairs:=nil; 307 | Entity.CountPairs:=0; 308 | 309 | Entity.ClassName:=''; 310 | Entity.TargetName:=''; 311 | Entity.isOrigin:=False; 312 | Entity.isAngles:=False; 313 | Entity.VisLeaf:=-1; 314 | Entity.BModel:=-1; 315 | {$R+} 316 | end; 317 | 318 | 319 | function FindFirstSpawnEntity(const Entities: PEntity; const Count: Integer): Integer; 320 | var 321 | i: Integer; 322 | begin 323 | {$R-} 324 | // First check that entities great then one 325 | if (Count <= 1) then 326 | begin 327 | Result:=-1; 328 | Exit; 329 | end; 330 | 331 | // First check player spawn 332 | for i:=1 to (Count - 1) do 333 | begin 334 | if (AEntity(Entities)[i].ClassName = ClassNameSpawnSP) then 335 | begin 336 | if (AEntity(Entities)[i].VisLeaf = 0) then Continue; 337 | Result:=i; 338 | Exit; 339 | end; 340 | end; 341 | 342 | // Next check DM spawn 343 | for i:=1 to (Count - 1) do 344 | begin 345 | if (AEntity(Entities)[i].ClassName = ClassNameSpawnDM) then 346 | begin 347 | if (AEntity(Entities)[i].VisLeaf = 0) then Continue; 348 | Result:=i; 349 | Exit; 350 | end; 351 | end; 352 | 353 | // Next check CT (CS/CSGO) spawn 354 | for i:=1 to (Count - 1) do 355 | begin 356 | if (AEntity(Entities)[i].ClassName = ClassNameSpawnCT) then 357 | begin 358 | if (AEntity(Entities)[i].VisLeaf = 0) then Continue; 359 | Result:=i; 360 | Exit; 361 | end; 362 | end; 363 | 364 | // Next check T (CS/CSGO) spawn 365 | for i:=1 to (Count - 1) do 366 | begin 367 | if (AEntity(Entities)[i].ClassName = ClassNameSpawnT) then 368 | begin 369 | if (AEntity(Entities)[i].VisLeaf = 0) then Continue; 370 | Result:=i; 371 | Exit; 372 | end; 373 | end; 374 | 375 | // if Bad then -1 376 | Result:=-1; 377 | {$R+} 378 | end; 379 | 380 | function GetPairIndexByKey(const Pairs: PKeyValue; const CountPairs: Integer; 381 | const Key: String): Integer; 382 | var 383 | i: Integer; 384 | begin 385 | {$R-} 386 | for i:=0 to (CountPairs - 1) do 387 | begin 388 | if (AKeyValue(Pairs)[i].Key = LowerCase(Key)) then 389 | begin 390 | Result:=i; 391 | Exit; 392 | end; 393 | end; 394 | Result:=-1; 395 | {$R+} 396 | end; 397 | 398 | function GetPairIndexByValue(const Pairs: PKeyValue; const CountPairs: Integer; 399 | const Value: String): Integer; 400 | var 401 | i: Integer; 402 | begin 403 | {$R-} 404 | for i:=0 to (CountPairs - 1) do 405 | begin 406 | if (AKeyValue(Pairs)[i].Value = Value) then 407 | begin 408 | Result:=i; 409 | Exit; 410 | end; 411 | end; 412 | Result:=-1; 413 | {$R+} 414 | end; 415 | 416 | function GetFirstPairIndexBySubValue(const Pairs: PKeyValue; const CountPairs: Integer; 417 | const SubValue: String): Integer; 418 | var 419 | i: Integer; 420 | begin 421 | {$R-} 422 | for i:=0 to (CountPairs - 1) do 423 | begin 424 | if (Pos(SubValue, AKeyValue(Pairs)[i].Value) > 0) then 425 | begin 426 | Result:=i; 427 | Exit; 428 | end; 429 | end; 430 | Result:=-1; 431 | {$R+} 432 | end; 433 | 434 | function FindEntityByClassName(const Entities: PEntity; const Count: Integer; 435 | const ClassNameKey: String): Integer; 436 | var 437 | i: Integer; 438 | begin 439 | {$R-} 440 | for i:=1 to (Count - 1) do 441 | begin 442 | if (AEntity(Entities)[i].ClassName = LowerCase(ClassNameKey)) then 443 | begin 444 | Result:=i; 445 | Exit; 446 | end; 447 | end; 448 | Result:=-1; 449 | {$R+} 450 | end; 451 | 452 | function FindEntityByTargetName(const Entities: PEntity; const Count: Integer; 453 | const TargetNameKey: String): Integer; 454 | var 455 | i: Integer; 456 | begin 457 | {$R-} 458 | for i:=1 to (Count - 1) do 459 | begin 460 | if (AEntity(Entities)[i].TargetName = LowerCase(TargetNameKey)) then 461 | begin 462 | Result:=i; 463 | Exit; 464 | end; 465 | end; 466 | Result:=-1; 467 | {$R+} 468 | end; 469 | 470 | function FindEntityByBModelIndex(const Entities: PEntity; const Count: Integer; 471 | const BModelIndex: Integer): Integer; 472 | var 473 | i: Integer; 474 | begin 475 | {$R-} 476 | if (BModelIndex < 0) then 477 | begin 478 | // it's point entity 479 | Result:=-1; 480 | Exit; 481 | end; 482 | 483 | for i:=0 to (Count - 1) do 484 | begin 485 | if (AEntity(Entities)[i].BModel = BModelIndex) then 486 | begin 487 | Result:=i; 488 | Exit; 489 | end; 490 | end; 491 | Result:=-1; 492 | {$R+} 493 | end; 494 | 495 | end. 496 | -------------------------------------------------------------------------------- /Modules/UnitBufferObjectManager.pas: -------------------------------------------------------------------------------- 1 | unit UnitBufferObjectManager; 2 | 3 | // Copyright (c) 2021 Sergey-KoRJiK, Belarus 4 | 5 | interface 6 | 7 | uses 8 | SysUtils, 9 | Windows, 10 | Classes, 11 | OpenGL, 12 | UnitOpenGLext; 13 | 14 | type CBufferObjectManager = class 15 | private 16 | iBufferObject: GLuint; 17 | eTarget: GLenum; 18 | eUsage: GLenum; // Only for Mutable Storage (glBufferData) 19 | bitFlags: GLbitfield; // Only for Immutable Storage (glBufferStorage) 20 | // 21 | iSize: GLsizeiptr; 22 | iMapPtrAlign: GLint; 23 | iBindIndex: GLint; 24 | // 25 | bIsCreated: Boolean; 26 | bIsBinded: Boolean; 27 | bIsImmutable: Boolean; 28 | bIsAllocated: Boolean; // mean first call of glBufferData or glBufferStorage 29 | bIsMapped: Boolean; 30 | public 31 | property IsCreated: Boolean read bIsCreated; 32 | property IsBinded: Boolean read bIsBinded; 33 | property IsImmutable: Boolean read bIsImmutable; 34 | property IsAllocated: Boolean read bIsAllocated; 35 | property IsMapped: Boolean read bIsMapped; 36 | // 37 | property BindedTarget: GLenum read eTarget; 38 | property MutableUsage: GLenum read eUsage; 39 | property ImmutableFlags: GLbitfield read bitFlags; 40 | property AllocatedSize: GLsizeiptr read iSize; 41 | property MappedPtrAlign: GLint read iMapPtrAlign; 42 | property BindedIndex: GLint read iBindIndex; 43 | // 44 | constructor CreateManager(); 45 | destructor DeleteManager(); 46 | // 47 | procedure Clear(); 48 | function CreateBuffer(): Boolean; 49 | function BindBuffer(const target: GLenum): Boolean; 50 | function BindBufferBase(const target: GLenum; const index: GLint): Boolean; 51 | procedure UnbindBuffer(); 52 | // 53 | function AllocateImmutable(const size: GLsizeiptr; const data: PGLvoid; 54 | const flags: GLbitfield): Boolean; 55 | function AllocateMutable(const size: GLsizeiptr; const data: PGLvoid; 56 | const usage: GLenum): Boolean; 57 | // 58 | function WriteToBuffer(const writeOffset: GLintptr; 59 | const writeSize: GLsizeiptr; const data: PGLvoid): Boolean; 60 | function ReadFromBuffer(const readOffset: GLintptr; 61 | const readSize: GLsizeiptr; const data: PGLvoid): Boolean; 62 | // 63 | function InvalidateBuffer(const offset: GLintptr; 64 | const size: GLsizeiptr): Boolean; 65 | function InternalCopyBuffer(const offsetSrc, offsetDst: GLintptr; 66 | const size: GLsizeiptr): Boolean; 67 | // 68 | function MapBuffer(const eAccess: GLenum): Pointer; 69 | function UnmapBuffer(): Boolean; 70 | end; 71 | 72 | 73 | implementation 74 | 75 | 76 | constructor CBufferObjectManager.CreateManager(); 77 | begin 78 | {$R-} 79 | inherited; 80 | 81 | Self.iBufferObject:=0; 82 | Self.eTarget:=0; 83 | Self.eUsage:=0; 84 | Self.bitFlags:=0; 85 | // 86 | Self.bIsCreated:=False; 87 | Self.bIsBinded:=False; 88 | Self.bIsImmutable:=False; 89 | Self.bIsAllocated:=False; 90 | // 91 | Self.iSize:=0; 92 | Self.iMapPtrAlign:=64; 93 | Self.iBindIndex:=-1; 94 | {$R+} 95 | end; 96 | 97 | destructor CBufferObjectManager.DeleteManager(); 98 | begin 99 | {$R-} 100 | glDeleteBuffersARB(1, @Self.iBufferObject); 101 | 102 | inherited; 103 | {$R+} 104 | end; 105 | 106 | 107 | procedure CBufferObjectManager.Clear(); 108 | begin 109 | {$R-} 110 | glDeleteBuffersARB(1, @Self.iBufferObject); 111 | Self.iBufferObject:=0; 112 | Self.eTarget:=0; 113 | Self.eUsage:=0; 114 | Self.bitFlags:=0; 115 | // 116 | Self.bIsCreated:=False; 117 | Self.bIsBinded:=False; 118 | Self.bIsImmutable:=False; 119 | Self.bIsAllocated:=False; 120 | // 121 | Self.iSize:=0; 122 | Self.iMapPtrAlign:=64; 123 | Self.iBindIndex:=-1; 124 | {$R+} 125 | end; 126 | 127 | function CBufferObjectManager.CreateBuffer(): Boolean; 128 | begin 129 | {$R-} 130 | Self.Clear(); 131 | glGenBuffersARB(1, @Self.iBufferObject); 132 | if (Self.iBufferObject <> 0) then Self.bIsCreated:=True; 133 | Result:=Self.bIsCreated; 134 | {$R+} 135 | end; 136 | 137 | function CBufferObjectManager.BindBuffer(const target: GLenum): Boolean; 138 | begin 139 | {$R-} 140 | if (not Self.bIsCreated) then 141 | begin 142 | Result:=False; 143 | Exit; 144 | end; 145 | 146 | Self.eTarget:=target; 147 | Self.iBindIndex:=-1; 148 | glBindBufferARB(Self.eTarget, Self.iBufferObject); 149 | Self.bIsBinded:=True; 150 | Result:=True; 151 | {$R+} 152 | end; 153 | 154 | function CBufferObjectManager.BindBufferBase(const target: GLenum; 155 | const index: GLint): Boolean; 156 | begin 157 | {$R-} 158 | if (not Self.bIsCreated) then 159 | begin 160 | Result:=False; 161 | Exit; 162 | end; 163 | 164 | Self.eTarget:=target; 165 | Self.iBindIndex:=index; 166 | glBindBufferBase(Self.eTarget, GLuint(Self.iBindIndex), Self.iBufferObject); 167 | Self.bIsBinded:=True; 168 | Result:=True; 169 | {$R+} 170 | end; 171 | 172 | procedure CBufferObjectManager.UnbindBuffer(); 173 | begin 174 | {$R-} 175 | glBindBufferARB(Self.eTarget, 0); 176 | Self.eTarget:=0; 177 | Self.bIsBinded:=False; 178 | {$R+} 179 | end; 180 | 181 | 182 | function CBufferObjectManager.AllocateImmutable(const size: GLsizeiptr; 183 | const data: PGLvoid; const flags: GLbitfield): Boolean; 184 | begin 185 | {$R-} 186 | if ((not Self.bIsBinded) or (Self.bIsAllocated) or (size <= 0)) then 187 | begin 188 | Result:=False; 189 | Exit; 190 | end; 191 | 192 | Self.iSize:=size; 193 | Self.bitFlags:=flags; 194 | glBufferStorage(Self.eTarget, Self.iSize, data, Self.bitFlags); 195 | Self.bIsAllocated:=True; 196 | Self.bIsImmutable:=True; 197 | Result:=True; 198 | {$R+} 199 | end; 200 | 201 | 202 | function CBufferObjectManager.AllocateMutable(const size: GLsizeiptr; 203 | const data: PGLvoid; const usage: GLenum): Boolean; 204 | begin 205 | {$R-} 206 | if ((not Self.bIsBinded) or (Self.bIsAllocated) or (size <= 0)) then 207 | begin 208 | Result:=False; 209 | Exit; 210 | end; 211 | 212 | Self.iSize:=size; 213 | Self.eUsage:=usage; 214 | glBufferDataARB(Self.eTarget, Self.iSize, data, Self.eUsage); 215 | Self.bIsAllocated:=True; 216 | Self.bIsImmutable:=False; 217 | Result:=True; 218 | {$R+} 219 | end; 220 | 221 | 222 | function CBufferObjectManager.WriteToBuffer(const writeOffset: GLintptr; 223 | const writeSize: GLsizeiptr; const data: PGLvoid): Boolean; 224 | begin 225 | {$R-} 226 | if ((not Self.bIsAllocated) or (data = nil) 227 | or ((writeOffset + writeSize) > Self.iSize) 228 | or (writeOffset < 0) or (writeSize <= 0)) then 229 | begin 230 | Result:=False; 231 | Exit; 232 | end; 233 | 234 | glBufferSubDataARB(Self.eTarget, writeOffset, writeSize, data); 235 | Result:=True; 236 | {$R+} 237 | end; 238 | 239 | function CBufferObjectManager.ReadFromBuffer(const readOffset: GLintptr; 240 | const readSize: GLsizeiptr; const data: PGLvoid): Boolean; 241 | begin 242 | {$R-} 243 | if ((not Self.bIsAllocated) or (data = nil) 244 | or ((readOffset + readSize) > Self.iSize) 245 | or (readOffset < 0) or (readSize <= 0)) then 246 | begin 247 | Result:=False; 248 | Exit; 249 | end; 250 | 251 | glGetBufferSubDataARB(Self.eTarget, readOffset, readSize, data); 252 | Result:=True; 253 | {$R+} 254 | end; 255 | 256 | 257 | function CBufferObjectManager.InvalidateBuffer(const offset: GLintptr; 258 | const size: GLsizeiptr): Boolean; 259 | begin 260 | {$R-} 261 | if ((not Self.bIsAllocated) or ((offset + size) > Self.iSize) 262 | or (offset < 0) or (size <= 0)) then 263 | begin 264 | Result:=False; 265 | Exit; 266 | end; 267 | 268 | glInvalidateBufferSubData(Self.iBufferObject, offset, size); 269 | Result:=True; 270 | {$R+} 271 | end; 272 | 273 | function CBufferObjectManager.InternalCopyBuffer(const offsetSrc, 274 | offsetDst: GLintptr; const size: GLsizeiptr): Boolean; 275 | begin 276 | {$R-} 277 | if ((not Self.bIsAllocated) or (offsetSrc < 0) or (offsetDst < 0) 278 | or (size <= 0) or (size > Self.iSize) or ((offsetDst + size) > Self.iSize) 279 | or ((offsetSrc + size) > Self.iSize)) then 280 | begin 281 | Result:=False; 282 | Exit; 283 | end; 284 | 285 | glCopyBufferSubData(Self.eTarget, Self.eTarget, offsetSrc, offsetDst, size); 286 | Result:=True; 287 | {$R+} 288 | end; 289 | 290 | 291 | function CBufferObjectManager.MapBuffer(const eAccess: GLenum): Pointer; 292 | begin 293 | {$R-} 294 | if ((not Self.bIsAllocated) or (Self.bIsMapped)) then 295 | begin 296 | Result:=nil; 297 | Exit; 298 | end; 299 | 300 | Self.bIsMapped:=True; 301 | Result:=glMapBufferARB(Self.eTarget, eAccess); 302 | {$R+} 303 | end; 304 | 305 | function CBufferObjectManager.UnmapBuffer(): Boolean; 306 | begin 307 | {$R-} 308 | if (Self.bIsMapped = False) then 309 | begin 310 | Result:=False; 311 | Exit; 312 | end; 313 | 314 | Self.iMapPtrAlign:=0; 315 | Self.bIsMapped:=False; 316 | Result:=glUnmapBufferARB(Self.eTarget); 317 | {$R+} 318 | end; 319 | 320 | end. 321 | -------------------------------------------------------------------------------- /Modules/UnitImageTGA.pas: -------------------------------------------------------------------------------- 1 | unit UnitImageTGA; 2 | 3 | // Copyright (c) 2020 Sergey-KoRJiK, Belarus 4 | // github.com/Sergey-KoRJiK 5 | 6 | interface 7 | 8 | uses 9 | SysUtils, 10 | Windows, 11 | Classes, 12 | UnitUserTypes, 13 | UnitVec; 14 | 15 | // For exporting lightmap megatextures and basetextures 16 | // need only support True-color 24/32 bit 17 | type tSimpleHeaderTGA = packed record 18 | LengthID : Byte; // 0x00 for less size 19 | ColorMapType : Byte; // 0x00 for true-color 24/32 bit 20 | ImageType : Byte; // 0x02 for true-color (0x0A for RLE per row) 21 | ColorMapLump : array[0..4] of Byte; // ColorMap properties, zero (unused) 22 | StartX : SmallInt; // 0 23 | StartY : SmallInt; // 0 24 | Width : SmallInt; 25 | Height : SmallInt; 26 | PixelDepthBit : Byte; // 24 or 32 for True-color image (RGB or RGBA) 27 | Flags : Byte; // 0x20; bits 0..3 = alpha channel depth, bit 5..6 - data order XY 28 | end; // 18 Bytes 29 | type PSimpleHeaderTGA = ^tSimpleHeaderTGA; 30 | type ASimpleHeaderTGA = array of tSimpleHeaderTGA; 31 | 32 | const 33 | TGA_MAX_SIZE = 1024; // 4 MBytes of max alloc size 34 | 35 | type CImageManagerTGA = class 36 | private 37 | rHeader: tSimpleHeaderTGA; 38 | iCountPixels: Integer; 39 | iPixelDataSize: Integer; 40 | lpPixelData: AByte; 41 | bAlphaChannel: Boolean; 42 | public 43 | property Width: SmallInt read rHeader.Width; 44 | property Height: SmallInt read rHeader.Height; 45 | property Area: Integer read iCountPixels; 46 | property IsTransparent: Boolean read bAlphaChannel; 47 | // 48 | constructor CreateManager(); 49 | destructor DeleteManager(); 50 | // 51 | procedure Clear(); 52 | // Reserve image (allocate memory) and set TGA header, if input size is valid 53 | function ReserveImage(const Width, Height: SmallInt; const bTransparent: Boolean): Boolean; 54 | procedure ClearImage(const FillColor: tRGBA8888); 55 | // 56 | procedure CopyFromRGBA(const lpSrc: PRGBA8888); 57 | procedure CopyFromRGB(const lpSrc: PRGB888; const Alpha: Byte); 58 | // 59 | procedure SaveToFile(const FileName: String); 60 | end; 61 | 62 | 63 | implementation 64 | 65 | 66 | constructor CImageManagerTGA.CreateManager(); 67 | begin 68 | {$R-} 69 | Self.lpPixelData:=nil; 70 | Self.Clear(); 71 | {$R+} 72 | end; 73 | 74 | destructor CImageManagerTGA.DeleteManager(); 75 | begin 76 | {$R-} 77 | Self.Clear(); 78 | {$R+} 79 | end; 80 | 81 | procedure CImageManagerTGA.Clear(); 82 | begin 83 | {$R-} 84 | ZeroFillChar(@Self.rHeader, SizeOf(tSimpleHeaderTGA)); 85 | Self.rHeader.ImageType:=2; 86 | Self.rHeader.PixelDepthBit:=24; 87 | Self.rHeader.Flags:=$20; 88 | Self.bAlphaChannel:=False; 89 | Self.iCountPixels:=0; 90 | Self.iPixelDataSize:=0; 91 | SetLength(Self.lpPixelData, 0); 92 | {$R+} 93 | end; 94 | 95 | function CImageManagerTGA.ReserveImage( 96 | const Width, Height: SmallInt; const bTransparent: Boolean): Boolean; 97 | begin 98 | {$R-} 99 | if ((Width <= 0) or (Height <=0) or (Width > TGA_MAX_SIZE) or (Height > TGA_MAX_SIZE)) then 100 | begin 101 | Result:=False; 102 | Exit; 103 | end; 104 | 105 | Self.Clear(); 106 | Self.rHeader.Width:=Width; 107 | Self.rHeader.Height:=Height; 108 | Self.bAlphaChannel:=bTransparent; 109 | Self.rHeader.PixelDepthBit:=24; 110 | if (Self.bAlphaChannel) then Self.rHeader.PixelDepthBit:=32; 111 | Self.iCountPixels:=Self.rHeader.Width*Self.rHeader.Height; 112 | Self.iPixelDataSize:=Self.iCountPixels*(Self.rHeader.PixelDepthBit shr 3); 113 | SetLength(Self.lpPixelData, Self.iPixelDataSize); 114 | 115 | Result:=True; 116 | {$R+} 117 | end; 118 | 119 | procedure CImageManagerTGA.ClearImage(const FillColor: tRGBA8888); 120 | var 121 | i: Integer; 122 | tmpRGB888: tRGB888; 123 | begin 124 | {$R-} 125 | if (Self.iPixelDataSize = 0) then Exit; 126 | if (Self.bAlphaChannel) then 127 | begin 128 | for i:=0 to (Self.iCountPixels - 1) do 129 | begin 130 | ARGBA8888(@Self.lpPixelData[0])[i]:=FillColor; 131 | end; 132 | end 133 | else 134 | begin 135 | tmpRGB888.r:=FillColor.r; 136 | tmpRGB888.g:=FillColor.g; 137 | tmpRGB888.b:=FillColor.b; 138 | for i:=0 to (Self.iCountPixels - 1) do 139 | begin 140 | ARGB888(@Self.lpPixelData[0])[i]:=tmpRGB888; 141 | end; 142 | end; 143 | {$R+} 144 | end; 145 | 146 | procedure CImageManagerTGA.CopyFromRGBA(const lpSrc: PRGBA8888); 147 | var 148 | i: Integer; 149 | begin 150 | {$R-} 151 | if ((Self.iPixelDataSize = 0) or (lpSrc = nil)) then Exit; 152 | if (Self.bAlphaChannel) then 153 | begin 154 | for i:=0 to (Self.iCountPixels - 1) do 155 | begin 156 | ARGBA8888(@Self.lpPixelData[0])[i]:=ARGBA8888(lpSrc)[i]; 157 | end; 158 | end 159 | else 160 | begin 161 | for i:=0 to (Self.iCountPixels - 1) do 162 | begin 163 | ARGB888(@Self.lpPixelData[0])[i].r:=ARGBA8888(lpSrc)[i].r; 164 | ARGB888(@Self.lpPixelData[0])[i].g:=ARGBA8888(lpSrc)[i].g; 165 | ARGB888(@Self.lpPixelData[0])[i].b:=ARGBA8888(lpSrc)[i].b; 166 | end; 167 | end; 168 | {$R+} 169 | end; 170 | 171 | procedure CImageManagerTGA.CopyFromRGB(const lpSrc: PRGB888; const Alpha: Byte); 172 | var 173 | i: Integer; 174 | begin 175 | {$R-} 176 | if ((Self.iPixelDataSize = 0) or (lpSrc = nil)) then Exit; 177 | if (Self.bAlphaChannel) then 178 | begin 179 | for i:=0 to (Self.iCountPixels - 1) do 180 | begin 181 | ARGBA8888(@Self.lpPixelData[0])[i].r:=ARGB888(lpSrc)[i].r; 182 | ARGBA8888(@Self.lpPixelData[0])[i].g:=ARGB888(lpSrc)[i].g; 183 | ARGBA8888(@Self.lpPixelData[0])[i].b:=ARGB888(lpSrc)[i].b; 184 | ARGBA8888(@Self.lpPixelData[0])[i].a:=Alpha; 185 | end; 186 | end 187 | else 188 | begin 189 | for i:=0 to (Self.iCountPixels - 1) do 190 | begin 191 | ARGB888(@Self.lpPixelData[0])[i]:=ARGB888(lpSrc)[i]; 192 | end; 193 | end; 194 | {$R+} 195 | end; 196 | 197 | procedure CImageManagerTGA.SaveToFile(const FileName: String); 198 | var 199 | TGAFile: File; 200 | begin 201 | {$R-} 202 | if ((FileName = '') or (Self.iPixelDataSize = 0)) then Exit; 203 | AssignFile(TGAFile, FileName); 204 | Rewrite(TGAFile, 1); 205 | 206 | BlockWrite(TGAFile, Self.rHeader, SizeOf(tSimpleHeaderTGA)); 207 | BlockWrite(TGAFile, Self.lpPixelData[0], Self.iPixelDataSize); 208 | 209 | CloseFile(TGAFile); 210 | {$R+} 211 | end; 212 | 213 | end. 214 | -------------------------------------------------------------------------------- /Modules/UnitMegatextureManager.pas: -------------------------------------------------------------------------------- 1 | unit UnitMegatextureManager; 2 | 3 | // Copyright (c) 2020 Sergey-KoRJiK, Belarus 4 | // github.com/Sergey-KoRJiK 5 | 6 | interface 7 | 8 | uses 9 | SysUtils, 10 | Windows, 11 | Classes, 12 | Graphics, 13 | OpenGL, 14 | UnitOpenGLext, 15 | UnitOpenGLErrorManager, 16 | UnitUserTypes, 17 | UnitVec; 18 | 19 | const 20 | MEGATEXTURE_SIZE = 1024; 21 | MEGATEXTURE_AREA = MEGATEXTURE_SIZE*MEGATEXTURE_SIZE; // pixels per texture 22 | MEGATEXTURE_MAX_COUNT = 128; // max count of megatextures 23 | MEGATEXTURE_MAX_REGIONS = 262144; // max regions per one megatexture 24 | MEGATEXTURE_STEP = 1/MEGATEXTURE_SIZE; // pixel step size by s, t. 25 | // 26 | MEGATEXTURE_DUMMY_MEGAID = 0; 27 | MEGATEXTURE_DUMMY_REGIONID = 0; 28 | MEGATEXTURE_DUMMY_SIZE: tVec2s = (X: 2; Y: 2); 29 | MEGATEXTURE_DUMMY_DATA: array[0..3] of tLightmap = ( 30 | (r: 0; g: 0; b: 0; e: 127;), 31 | (r: 0; g: 0; b: 0; e: 127;), 32 | (r: 0; g: 0; b: 0; e: 127;), 33 | (r: 0; g: 0; b: 0; e: 127;) 34 | ); // black with max exponent is unsupported by Source, but 35 | // here used for mark Faces without lightmap, fullbright 36 | MEGATEXTURE_DUMMY_COORD: tVec2f = ( 37 | x: 0.0; y: 0.0; 38 | ); 39 | 40 | 41 | type tSubRegion = packed record 42 | bMin, bMax: tVec2s; 43 | end; // 8 Bytes 44 | type PSubRegion = ^tSubRegion; 45 | type ASubRegion = array of tSubRegion; 46 | 47 | const 48 | MEGATEXTURE_DUMMY_SUBREGION: tSubRegion = ( 49 | bMin: (x: 0; y: 0); bMax: (x: 2; y: 2); 50 | ); 51 | MEGATEXTURE_MEMSIZE = MEGATEXTURE_MAX_REGIONS*SizeOf(tSubRegion); 52 | 53 | type tMegatexture = packed record 54 | CountRegions: DWORD; 55 | FirstRegionMax: tVec2s; 56 | Regions: array[0..MEGATEXTURE_MAX_REGIONS-2] of tSubRegion; 57 | end; // 2 MBytes 58 | type PMegatexture = ^tMegatexture; 59 | type AMegatexture = array of tMegatexture; 60 | 61 | const 62 | SUBREGION_NULL: tSubRegion = (bMin: (x: 0; y: 0); bMax: (x: 0; y: 0)); 63 | SUBREGION_CLIP: tSubRegion = ( 64 | bMin: (x: 0; y: 0); 65 | bMax: (x: MEGATEXTURE_SIZE-1; y: MEGATEXTURE_SIZE-1) 66 | ); 67 | 68 | type 69 | eMegaMemError = ( 70 | mmeNoError = 0, 71 | mmeOutOfRange = 1, 72 | mmeOutOfMemory = 2, 73 | mmeOutOfGPUMemory = 3 74 | ); 75 | PMegaMemError = ^eMegaMemError; 76 | 77 | 78 | type CMegatextureManager = class 79 | private 80 | iCountPages: Integer; // Counter of pages 81 | iPageIndex: Integer; // iCountPages - 1 82 | iTotalTextures: Integer; // Counter of total inserted textures 83 | // List of 32-bit Pointers 84 | PageList: array[0..MEGATEXTURE_MAX_COUNT-1] of PMegatexture; 85 | CurrPixelBuff: array[0..MEGATEXTURE_AREA-1] of tLightmap; 86 | CurrPage: PMegatexture; // temporary page iterator 87 | CurrRegion: PSubRegion; // temporary regiomn iterator 88 | // List of OpenGL 2D Textures Id 89 | Page2DList: array[0..MEGATEXTURE_MAX_COUNT-1] of GLuint; 90 | CurrBindedMegatextureIndex: Integer; 91 | // List of reserved heights in current page 92 | ScanLine: array[0..MEGATEXTURE_SIZE-1] of SmallInt; 93 | // 94 | bLinearFilter: Boolean; 95 | bEnable: Boolean; 96 | vsMaxRegionSize: tVec2s; 97 | // 98 | procedure ClearReservedArea(); 99 | function GetRegion(const MegatextureId, SubRegionId: Integer): tSubRegion; 100 | function GetRegionCount(const MegatextureId: Integer): Integer; 101 | public 102 | property CountMegatextures: Integer read iCountPages; 103 | property TotalInsertedTextures: Integer read iTotalTextures; 104 | property CurrentMegatextureIndex: Integer read iPageIndex; 105 | property IsMinAndMagLinearFiltering: Boolean read bLinearFilter; 106 | property IsEnableToRender: Boolean read bEnable; 107 | // 108 | property CountOfRegions[const MegatextureId: Integer]: Integer read GetRegionCount; 109 | property TextureRegion[const MegatextureId, SubRegionId: Integer]: tSubRegion read GetRegion; 110 | // 111 | property MaxRegionSize: tVec2s read vsMaxRegionSize; 112 | property MaxRegionSizeX: SmallInt read vsMaxRegionSize.x; 113 | property MaxRegionSizeY: SmallInt read vsMaxRegionSize.y; 114 | // 115 | constructor CreateManager(); 116 | destructor DeleteManager(); 117 | // 118 | procedure Clear(); 119 | function AllocNewMegatexture(const ErrorInfo: PMegaMemError): Boolean; 120 | // 121 | function IsCanReserveTexture(const Size: tVec2s): Boolean; 122 | function ReserveTexture(const Size: tVec2s): Integer; // Return SubRegion id 123 | // 124 | //function UpdateTextureFromArray(const MegatextureId, SubRegionId: Integer; 125 | // const SrcData: PLightmap): Boolean; 126 | function UpdateCurrentBufferFromArray(const MegatextureId, SubRegionId: Integer; 127 | const SrcData: PLightmap; const RegionOffset, OverrideSize: PVec2s): Boolean; 128 | function UpdateTextureFromCurrentBuffer(): Boolean; 129 | // 130 | procedure BindMegatexture2D(const MegatextureIndex: Integer); 131 | procedure UnbindMegatexture2D(); 132 | // 133 | function UpdateTextureCoords(const MegatextureId, SubRegionId: Integer; 134 | const CoordSrc: PVec2f; const CoordDest: PVec2f; 135 | const CountTexCoords: Integer): Boolean; 136 | // 137 | procedure SetFiltrationMode(const isLinearFiltration: Boolean); 138 | procedure SetLightmapState(const isEnable: Boolean); 139 | end; 140 | 141 | 142 | function GetMegaMemErrorInfo(const MegaMemError: eMegaMemError): String; 143 | 144 | 145 | implementation 146 | 147 | 148 | function GetMegaMemErrorInfo(const MegaMemError: eMegaMemError): String; 149 | begin 150 | {$R-} 151 | case (MegaMemError) of 152 | mmeNoError: Result:='No errors'; 153 | mmeOutOfRange: Result:='Error allocate new megatexture: reached max ' 154 | + IntToStr(MEGATEXTURE_MAX_COUNT) + ' megatextures!'; 155 | mmeOutOfMemory: Result:='Error allocate new megatexture: Out of Memory!'; 156 | mmeOutOfGPUMemory: Result:='Error allocate new megatexture: Out of GPU Memory!'; 157 | else 158 | Result:='Unknow error'; 159 | end; 160 | {$R+} 161 | end; 162 | 163 | 164 | constructor CMegatextureManager.CreateManager(); 165 | begin 166 | {$R-} 167 | Self.iCountPages:=0; 168 | Self.iPageIndex:=-1; 169 | Self.iTotalTextures:=0; 170 | Self.CurrBindedMegatextureIndex:=-1; 171 | Self.CurrPage:=nil; 172 | Self.CurrRegion:=nil; 173 | Self.bLinearFilter:=True; 174 | Self.bEnable:=True; 175 | Self.vsMaxRegionSize:=VEC_ZERO_2S; 176 | ZeroFillChar(@Self.PageList[0], MEGATEXTURE_MAX_COUNT*4); 177 | ZeroFillChar(@Self.Page2DList[0], MEGATEXTURE_MAX_COUNT*4); 178 | Self.ClearReservedArea; 179 | {$R+} 180 | end; 181 | 182 | destructor CMegatextureManager.DeleteManager(); 183 | begin 184 | {$R-} 185 | Self.Clear(); 186 | {$R+} 187 | end; 188 | 189 | 190 | procedure CMegatextureManager.ClearReservedArea(); 191 | begin 192 | {$R-} 193 | ZeroFillChar(@Self.ScanLine[0], SizeOf(Self.ScanLine)); 194 | ZeroFillChar(@Self.CurrPixelBuff[0], SizeOf(Self.CurrPixelBuff)); 195 | {$R+} 196 | end; 197 | 198 | function CMegatextureManager.GetRegion(const MegatextureId, SubRegionId: Integer): tSubRegion; 199 | begin 200 | {$R-} 201 | if ((MegatextureId < 0) or (MegatextureId >= Self.iCountPages) 202 | or (SubRegionId < 0)) then 203 | begin 204 | Result:=SUBREGION_NULL; 205 | Exit; 206 | end; 207 | 208 | if (DWORD(SubRegionId) >= Self.PageList[MegatextureId].CountRegions) then 209 | begin 210 | Result:=SUBREGION_NULL; 211 | Exit; 212 | end; 213 | 214 | if (SubRegionId = 0) then 215 | begin 216 | Result.bMin:=VEC_ZERO_2S; 217 | Result.bMax:=PageList[MegatextureId].FirstRegionMax; 218 | end 219 | else 220 | begin 221 | Result:=Self.PageList[MegatextureId].Regions[SubRegionId - 1]; 222 | end; 223 | {$R+} 224 | end; 225 | 226 | function CMegatextureManager.GetRegionCount(const MegatextureId: Integer): Integer; 227 | begin 228 | {$R-} 229 | if ((MegatextureId < 0) or (MegatextureId >= Self.iCountPages)) then 230 | begin 231 | Result:=0; 232 | end 233 | else 234 | begin 235 | Result:=PageList[MegatextureId].CountRegions; 236 | end; 237 | {$R+} 238 | end; 239 | 240 | 241 | procedure CMegatextureManager.Clear(); 242 | var 243 | i: Integer; 244 | begin 245 | {$R-} 246 | for i:=0 to (Self.iCountPages - 1) do 247 | begin 248 | if (Self.PageList[i] <> nil) then 249 | begin 250 | //VirtualFree(Self.PageList[i], 0, MEM_RELEASE); 251 | SysFreeMem(Self.PageList[i]); 252 | Self.PageList[i]:=nil; 253 | // 254 | if (Self.Page2DList[i] <> 0) then glDeleteTextures(1, @Self.Page2DList[i]); 255 | Self.Page2DList[i]:=0; 256 | end; 257 | end; 258 | // 259 | Self.ClearReservedArea; 260 | Self.iCountPages:=0; 261 | Self.iPageIndex:=-1; 262 | Self.iTotalTextures:=0; 263 | Self.CurrBindedMegatextureIndex:=-1; 264 | Self.CurrPage:=nil; 265 | Self.CurrRegion:=nil; 266 | Self.vsMaxRegionSize:=VEC_ZERO_2S; 267 | {$R+} 268 | end; 269 | 270 | function CMegatextureManager.AllocNewMegatexture(const ErrorInfo: PMegaMemError): Boolean; 271 | var 272 | tmp: PMegatexture; 273 | begin 274 | {$R-} 275 | if (Self.iCountPages >= MEGATEXTURE_MAX_COUNT) then 276 | begin 277 | if (ErrorInfo <> nil) then ErrorInfo^:=mmeOutOfRange; 278 | Result:=False; 279 | Exit; 280 | end; 281 | 282 | //tmp:=VirtualAlloc(nil, MEGATEXTURE_MEMSIZE, MEM_COMMIT or MEM_RESERVE, PAGE_READWRITE); 283 | tmp:=SysGetMem(MEGATEXTURE_MEMSIZE); 284 | if (tmp <> nil) then 285 | begin 286 | Inc(Self.iPageIndex); 287 | Inc(Self.iCountPages); 288 | // 289 | Self.CurrPage:=tmp; 290 | Self.CurrRegion:=@Self.CurrPage.Regions[0]; 291 | Self.PageList[Self.iPageIndex]:=Self.CurrPage; 292 | Self.CurrPage.CountRegions:=0; 293 | Self.ClearReservedArea; 294 | 295 | // Create texture 296 | glGenTextures(1, @Self.Page2DList[Self.iPageIndex]); 297 | if (Self.Page2DList[Self.iPageIndex] = 0) then 298 | begin 299 | if (ErrorInfo <> nil) then ErrorInfo^:=mmeOutOfGPUMemory; 300 | Result:=False; 301 | Exit; 302 | end; 303 | 304 | glActiveTextureARB(GL_TEXTURE0); 305 | glEnable(GL_TEXTURE_2D); 306 | glBindTexture(GL_TEXTURE_2D, Self.Page2DList[Self.iPageIndex]); 307 | // GL_CLAMP_TO_EDGE / GL_REPEAT 308 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); 309 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 310 | // GL_LINEAR / GL_NEAREST 311 | if (Self.bLinearFilter) then 312 | begin 313 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 314 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 315 | end 316 | else 317 | begin 318 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 319 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 320 | end; 321 | // 322 | // 323 | // Reserve GPU memory 324 | glTexImage2D( 325 | GL_TEXTURE_2D, 0, GL_RGBA8, 326 | MEGATEXTURE_SIZE, MEGATEXTURE_SIZE, 327 | 0, 328 | GL_RGBA, GL_UNSIGNED_BYTE, 329 | nil 330 | ); 331 | // 332 | glBindTexture(GL_TEXTURE_2D, 0); //} 333 | 334 | if (ErrorInfo <> nil) then ErrorInfo^:=mmeNoError; 335 | Result:=True; 336 | end 337 | else 338 | begin 339 | // Out of Memory 340 | if (ErrorInfo <> nil) then ErrorInfo^:=mmeOutOfMemory; 341 | Result:=False; 342 | end; 343 | {$R+} 344 | end; 345 | 346 | 347 | function CMegatextureManager.IsCanReserveTexture(const Size: tVec2s): Boolean; 348 | var 349 | i, j: SmallInt; 350 | best, best2: SmallInt; 351 | begin 352 | {$R-} 353 | if ((Size.x < 2) or (Size.y < 2) or (Size.x > MEGATEXTURE_SIZE) 354 | or (Size.y > MEGATEXTURE_SIZE)) then 355 | begin 356 | // We only work with sizes from 2x2 to 128x128 357 | Result:=False; 358 | Exit; 359 | end; 360 | 361 | if (Self.CurrPage.CountRegions = 0) then 362 | begin 363 | // In this case this is first region and it's always start at (0, 0) 364 | // and we reject input sizes out of range [2x2 .. 1024x1024]. 365 | // Thus we can put him at (0, 0) 366 | Result:=True; 367 | Exit; 368 | end; 369 | 370 | best:=MEGATEXTURE_SIZE; 371 | for i:=0 to (MEGATEXTURE_SIZE - Size.x - 1) do 372 | begin 373 | best2:=0; 374 | for j:=0 to (Size.x - 1) do 375 | begin 376 | if (Self.ScanLine[i + j] >= best) then 377 | begin 378 | Break; 379 | end; 380 | if (Self.ScanLine[i + j] > best2) then 381 | begin 382 | best2:=Self.ScanLine[i + j]; 383 | end; 384 | end; 385 | if (j = Size.x) then 386 | begin 387 | // this is a valid spot 388 | best:=best2; 389 | end; 390 | end; 391 | 392 | Result:=((best + Size.y) <= MEGATEXTURE_SIZE); 393 | {$R+} 394 | end; 395 | 396 | function CMegatextureManager.ReserveTexture(const Size: tVec2s): Integer; 397 | var 398 | Pos: tVec2s; 399 | i, j: Integer; 400 | best, best2: Integer; 401 | begin 402 | {$R-} 403 | if ((Size.x < 2) or (Size.y < 2) or (Size.x > MEGATEXTURE_SIZE) 404 | or (Size.y > MEGATEXTURE_SIZE)) then 405 | begin 406 | // We only work with sizes from 2x2 to 1024x1024 407 | Result:=-1; 408 | Exit; 409 | end; 410 | 411 | if (Self.CurrPage.CountRegions = 0) then 412 | begin 413 | // In this case this is first region and it's always start at (0, 0) 414 | // and we reject input sizes out of range [2x2 .. 1024x1024]. 415 | // Thus we can put him at (0, 0), do it: 416 | Self.CurrPage.CountRegions:=1; 417 | Self.CurrPage.FirstRegionMax.x:=Byte(Size.x - 1); 418 | Self.CurrPage.FirstRegionMax.y:=Byte(Size.y - 1); 419 | for i:=0 to (Size.x - 1) do 420 | begin 421 | Self.ScanLine[i]:=Size.y; 422 | end; 423 | Inc(Self.iTotalTextures); 424 | 425 | Result:=0; 426 | Exit; 427 | end; 428 | 429 | best:=MEGATEXTURE_SIZE; 430 | for i:=0 to (MEGATEXTURE_SIZE - Size.x - 1) do 431 | begin 432 | best2:=0; 433 | for j:=0 to (Size.x - 1) do 434 | begin 435 | if (Self.ScanLine[i + j] >= best) then 436 | begin 437 | Break; 438 | end; 439 | if (Self.ScanLine[i + j] > best2) then 440 | begin 441 | best2:=Self.ScanLine[i + j]; 442 | end; 443 | end; 444 | if (j = Size.x) then 445 | begin 446 | // this is a valid spot 447 | Pos.x:=i; 448 | Pos.y:=best2; 449 | best:=best2; 450 | end; 451 | end; 452 | 453 | // Check if put region get out of megatexture size range. 454 | if ((best + Size.y) > MEGATEXTURE_SIZE) then 455 | begin 456 | Result:=-1; 457 | Exit; 458 | end; 459 | 460 | // Update ScanLine 461 | Inc(best, Size.y); 462 | for i:=0 to (Size.x - 1) do 463 | begin 464 | Self.ScanLine[Pos.x + i]:=best; 465 | end; 466 | 467 | Self.CurrRegion.bMin.x:=Pos.x; 468 | Self.CurrRegion.bMin.y:=Pos.y; 469 | Self.CurrRegion.bMax.x:=(Size.x - 1) + Pos.x; 470 | Self.CurrRegion.bMax.y:=(Size.y - 1) + Pos.y; 471 | if (Size.x > Self.vsMaxRegionSize.x) then Self.vsMaxRegionSize.x:=Size.x; 472 | if (Size.y > Self.vsMaxRegionSize.y) then Self.vsMaxRegionSize.y:=Size.y; 473 | Inc(Self.CurrRegion); 474 | Inc(Self.CurrPage.CountRegions); 475 | Inc(Self.iTotalTextures); 476 | 477 | Result:=Self.CurrPage.CountRegions - 1; 478 | {$R+} 479 | end; 480 | 481 | 482 | (*function CMegatextureManager.UpdateTextureFromArray(const MegatextureId, SubRegionId: Integer; 483 | const SrcData: PLightmap): Boolean; 484 | var 485 | SubRegion: tSubRegion; 486 | RenderId: GLuint; 487 | begin 488 | {$R-} 489 | if ((MegatextureId < 0) or (MegatextureId >= Self.iCountPages) 490 | or (SubRegionId < 0) or (SubRegionId >= MEGATEXTURE_MAX_REGIONS)) then 491 | begin 492 | Result:=False; 493 | Exit; 494 | end; 495 | 496 | RenderId:=Self.Page2DList[MegatextureId]; 497 | if (RenderId = 0) then 498 | begin 499 | Result:=False; 500 | Exit; 501 | end; 502 | 503 | if (DWORD(SubRegionId) >= Self.PageList[MegatextureId].CountRegions) then 504 | begin 505 | Result:=False; 506 | Exit; 507 | end; 508 | 509 | if (SubRegionId = 0) then 510 | begin 511 | SubRegion.bMin:=VEC_ZERO_2S; 512 | SubRegion.bMax:=Self.PageList[MegatextureId].FirstRegionMax; 513 | end 514 | else 515 | begin 516 | SubRegion:=Self.PageList[MegatextureId].Regions[SubRegionId - 1]; 517 | end; 518 | 519 | 520 | glActiveTextureARB(GL_TEXTURE0); 521 | glEnable(GL_TEXTURE_2D); 522 | glBindTexture(GL_TEXTURE_2D, RenderId); 523 | glTexSubImage2D( 524 | GL_TEXTURE_2D, 0, 525 | SubRegion.bMin.x, 526 | SubRegion.bMin.y, 527 | (SubRegion.bMax.x - SubRegion.bMin.x + 1), 528 | (SubRegion.bMax.y - SubRegion.bMin.y + 1), 529 | GL_RGBA, GL_UNSIGNED_BYTE, 530 | SrcData 531 | ); 532 | glBindTexture(GL_TEXTURE_2D, 0); 533 | 534 | Result:=True; 535 | {$R+} 536 | end; //*) 537 | 538 | function CMegatextureManager.UpdateCurrentBufferFromArray(const MegatextureId, SubRegionId: Integer; 539 | const SrcData: PLightmap; const RegionOffset, OverrideSize: PVec2s): Boolean; 540 | var 541 | i, j: Integer; 542 | PtrSrcData, PtrDest: PLightmap; 543 | SubRegion: tSubRegion; 544 | begin 545 | {$R-} 546 | if ((MegatextureId < 0) or (MegatextureId >= Self.iCountPages) 547 | or (SubRegionId < 0) or (SubRegionId >= MEGATEXTURE_MAX_REGIONS)) then 548 | begin 549 | Result:=False; 550 | Exit; 551 | end; 552 | 553 | if (DWORD(SubRegionId) >= Self.PageList[MegatextureId].CountRegions) then 554 | begin 555 | Result:=False; 556 | Exit; 557 | end; 558 | 559 | if (SubRegionId = 0) then 560 | begin 561 | SubRegion.bMin:=VEC_ZERO_2S; 562 | SubRegion.bMax:=Self.PageList[MegatextureId].FirstRegionMax; 563 | end 564 | else 565 | begin 566 | SubRegion:=Self.PageList[MegatextureId].Regions[SubRegionId - 1]; 567 | end; 568 | 569 | if (OverrideSize <> nil) then 570 | begin 571 | SubRegion.bMax.x:=SubRegion.bMin.x + OverrideSize.x - 1; 572 | SubRegion.bMax.y:=SubRegion.bMin.y + OverrideSize.y - 1; 573 | end; 574 | 575 | if (RegionOffset <> nil) then if ( 576 | ((RegionOffset.x + SubRegion.bMax.x) < MEGATEXTURE_SIZE) AND 577 | ((RegionOffset.y + SubRegion.bMax.y) < MEGATEXTURE_SIZE) 578 | ) then begin 579 | Inc(SubRegion.bMin.x, RegionOffset.x); 580 | Inc(SubRegion.bMin.y, RegionOffset.y); 581 | Inc(SubRegion.bMax.x, RegionOffset.x); 582 | Inc(SubRegion.bMax.y, RegionOffset.y); 583 | end; 584 | 585 | PtrSrcData:=SrcData; 586 | PtrDest:=@Self.CurrPixelBuff[SubRegion.bMin.y*MEGATEXTURE_SIZE + SubRegion.bMin.x]; 587 | j:=SubRegion.bMax.x - SubRegion.bMin.x + 1; 588 | for i:=0 to (SubRegion.bMax.y - SubRegion.bMin.y) do 589 | begin 590 | //CopyLightmaps(PtrSrcData, PtrDest, j); 591 | CopyLightmapsFixExp(PtrSrcData, PtrDest, j); 592 | Inc(PtrSrcData, j); 593 | Inc(PtrDest, MEGATEXTURE_SIZE); 594 | end; 595 | 596 | Result:=True; 597 | {$R+} 598 | end; 599 | 600 | function CMegatextureManager.UpdateTextureFromCurrentBuffer(): Boolean; 601 | var 602 | RenderId: GLuint; 603 | begin 604 | {$R-} 605 | if (Self.iPageIndex < 0) then 606 | begin 607 | Result:=False; 608 | Exit; 609 | end; 610 | 611 | RenderId:=Self.Page2DList[Self.iPageIndex]; 612 | if (RenderId = 0) then 613 | begin 614 | Result:=False; 615 | Exit; 616 | end; 617 | 618 | glActiveTextureARB(GL_TEXTURE0); 619 | glEnable(GL_TEXTURE_2D); 620 | glBindTexture(GL_TEXTURE_2D, RenderId); 621 | glTexSubImage2D( 622 | GL_TEXTURE_2D, 0, 623 | 0, 0, 624 | MEGATEXTURE_SIZE, MEGATEXTURE_SIZE, 625 | GL_RGBA, GL_UNSIGNED_BYTE, 626 | @Self.CurrPixelBuff[0] 627 | ); 628 | glBindTexture(GL_TEXTURE_2D, 0); 629 | 630 | Result:=True; 631 | {$R+} 632 | end; 633 | 634 | procedure CMegatextureManager.BindMegatexture2D(const MegatextureIndex: Integer); 635 | begin 636 | {$R-} 637 | if ((MegatextureIndex >= 0) and (MegatextureIndex < Self.iCountPages) and (Self.bEnable)) then 638 | begin 639 | if (MegatextureIndex <> Self.CurrBindedMegatextureIndex) then 640 | begin 641 | Self.CurrBindedMegatextureIndex:=MegatextureIndex; 642 | glActiveTextureARB(GL_TEXTURE0); 643 | glBindTexture(GL_TEXTURE_2D, Self.Page2DList[Self.CurrBindedMegatextureIndex]); 644 | end; 645 | end 646 | else 647 | begin 648 | Self.CurrBindedMegatextureIndex:=-1; 649 | glActiveTextureARB(GL_TEXTURE0); 650 | glBindTexture(GL_TEXTURE_2D, 0); 651 | end; 652 | {$R+} 653 | end; 654 | 655 | procedure CMegatextureManager.UnbindMegatexture2D(); 656 | begin 657 | {$R-} 658 | Self.CurrBindedMegatextureIndex:=-1; 659 | glActiveTextureARB(GL_TEXTURE0); 660 | glBindTexture(GL_TEXTURE_2D, 0); 661 | {$R+} 662 | end; 663 | 664 | 665 | function CMegatextureManager.UpdateTextureCoords( 666 | const MegatextureId, SubRegionId: Integer; 667 | const CoordSrc: PVec2f; const CoordDest: PVec2f; 668 | const CountTexCoords: Integer): Boolean; 669 | var 670 | i: Integer; 671 | CurrSubRegion: tSubRegion; 672 | begin 673 | {$R-} 674 | if ((MegatextureId < 0) or (MegatextureId >= Self.iCountPages) 675 | or (SubRegionId < 0) or (SubRegionId >= MEGATEXTURE_MAX_REGIONS) 676 | or (CountTexCoords <= 0)) then 677 | begin 678 | Result:=False; 679 | Exit; 680 | end; 681 | 682 | if (DWORD(SubRegionId) >= Self.PageList[MegatextureId].CountRegions) then 683 | begin 684 | Result:=False; 685 | Exit; 686 | end; 687 | 688 | if (SubRegionId = 0) then 689 | begin 690 | CurrSubRegion.bMin:=VEC_ZERO_2S; 691 | CurrSubRegion.bMax:=Self.PageList[MegatextureId].FirstRegionMax; 692 | end 693 | else 694 | begin 695 | CurrSubRegion:=Self.PageList[MegatextureId].Regions[SubRegionId - 1]; 696 | end; 697 | 698 | for i:=0 to (CountTexCoords - 1) do 699 | begin 700 | AVec2f(CoordDest)[i].x:=(AVec2f(CoordSrc)[i].x + CurrSubRegion.bMin.x + 0.5)*MEGATEXTURE_STEP; 701 | AVec2f(CoordDest)[i].y:=(AVec2f(CoordSrc)[i].y + CurrSubRegion.bMin.y + 0.5)*MEGATEXTURE_STEP; 702 | end; //} 703 | 704 | Result:=True; 705 | {$R+} 706 | end; 707 | 708 | 709 | procedure CMegatextureManager.SetFiltrationMode(const isLinearFiltration: Boolean); 710 | var 711 | i: Integer; 712 | begin 713 | {$R-} 714 | Self.bLinearFilter:=isLinearFiltration; 715 | glActiveTextureARB(GL_TEXTURE0); 716 | if (isLinearFiltration) then 717 | begin 718 | for i:=0 to (Self.iCountPages - 1) do 719 | begin 720 | glBindTexture(GL_TEXTURE_2D, Self.Page2DList[i]); 721 | // GL_LINEAR / GL_NEAREST 722 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 723 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 724 | end; 725 | end 726 | else 727 | begin 728 | for i:=0 to (Self.iCountPages - 1) do 729 | begin 730 | glBindTexture(GL_TEXTURE_2D, Self.Page2DList[i]); 731 | // GL_LINEAR / GL_NEAREST 732 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 733 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 734 | end; 735 | end; 736 | glBindTexture(GL_TEXTURE_2D, 0); 737 | {$R+} 738 | end; 739 | 740 | procedure CMegatextureManager.SetLightmapState(const isEnable: Boolean); 741 | begin 742 | {$R-} 743 | Self.bEnable:=isEnable; 744 | if (isEnable = False) then Self.UnbindMegatexture2D(); 745 | {$R+} 746 | end; 747 | 748 | 749 | end. 750 | -------------------------------------------------------------------------------- /Modules/UnitOpenGLErrorManager.pas: -------------------------------------------------------------------------------- 1 | unit UnitOpenGLErrorManager; 2 | 3 | // Copyright (c) 2020 Sergey-KoRJiK, Belarus 4 | // github.com/Sergey-KoRJiK 5 | 6 | interface 7 | 8 | uses 9 | Windows, 10 | OpenGL; 11 | 12 | // Clear error queue 13 | procedure glClearErrorQueue(); 14 | 15 | // Get list of last errors of OpenGL error queue. 16 | // Max size of list is 16384 Errors. Ff errors more then queue limit, 17 | // then function return False. 18 | function glScanErrors(): ByteBool; 19 | 20 | // Get upper error from scanned queue and remove this from queue 21 | function glPopError(): GLenum; 22 | 23 | // Get upper error from scanned queue 24 | function glReadUpperError(): GLenum; 25 | 26 | // Get current count of scanned errors into queue 27 | function glGetCountErrors(): Integer; 28 | 29 | // Return errors into string 30 | function glGetStrErrors(const ErrStrSeparator: Char): String; 31 | 32 | // Call glScanErrors, next show message dialog by glGetStrErrors() 33 | // where separator is new line, and finish by clear error queue. 34 | procedure glShowErrorsMessageBox(); 35 | 36 | 37 | implementation 38 | 39 | 40 | const 41 | STR_UNKNOW_ = 'UNKNOW_'; // 7 42 | STR_NO_ERROR = 'NO_ERROR'; // 8 43 | STR_INVALID_ENUM = 'INVALID_ENUM'; // 12 44 | STR_INVALID_VALUE = 'INVALID_VALUE'; // 13 45 | STR_INVALID_OPERATION = 'INVALID_OPERATION'; // 17 46 | STR_STACK_OVERFLOW = 'STACK_OVERFLOW'; // 14 47 | STR_STACK_UNDERFLOW = 'STACK_UNDERFLOW'; // 15 48 | STR_OUT_OF_MEMORY = 'OUT_OF_MEMORY'; // 13 49 | STR_INVALID_FRAMEBUFFER_OPERATION = 'INVALID_FRAMEBUFFER_OPERATION'; // 29 50 | STR_CONTEXT_LOST = 'CONTEXT_LOST'; // 12 51 | STR_TABLE_TOO_LARGE = 'TABLE_TOO_LARGE'; // 15 52 | // 53 | GL_INVALID_FRAMEBUFFER_OPERATION = $0506; 54 | GL_CONTEXT_LOST = $0507; 55 | GL_TABLE_TOO_LARGE = $8031; 56 | 57 | type PGLenum = ^GLenum; 58 | 59 | var 60 | glErrorQueue, glUpperError, glUpperLimit: PGLenum; 61 | 62 | procedure glClearErrorQueue(); 63 | asm 64 | {$R-} 65 | push EDI 66 | mov EDI, glErrorQueue 67 | mov glUpperError, EDI 68 | xor EAX, EAX // Filler EAX = 0 69 | mov ECX, $00004000 // $4000 = 16384 = 65536 / 4; 70 | rep stosd 71 | // rep: 72 | // 1. MOV [EDX], EAX 73 | // 2. EDI = EDI + 4 74 | // 3. ECX = ECX - 1 75 | // 4. if (ECX <> 0) go to step 1. 76 | pop EDI 77 | {$R+} 78 | end; 79 | 80 | function glScanErrors(): ByteBool; 81 | begin 82 | {$R-} 83 | if (glErrorQueue = nil) then 84 | begin 85 | Result:=False; 86 | Exit; 87 | end; 88 | 89 | while (glUpperError <> glUpperLimit) do 90 | begin 91 | glUpperError^:=glGetError(); 92 | if (glUpperError^ = GL_NO_ERROR) then Break; 93 | Inc(glUpperError); 94 | end; 95 | Result:=(glUpperError = glUpperLimit); 96 | if (glUpperError^ <> GL_NO_ERROR) then Dec(glUpperError); 97 | {$R+} 98 | end; 99 | 100 | function glPopError(): GLenum; 101 | begin 102 | {$R-} 103 | if ((glErrorQueue = nil) or (glUpperError = glErrorQueue)) then 104 | begin 105 | Result:=GL_NO_ERROR; 106 | Exit; 107 | end; 108 | 109 | Result:=glUpperError^; 110 | Dec(glUpperError); 111 | {$R+} 112 | end; 113 | 114 | function glReadUpperError(): GLenum; 115 | begin 116 | {$R-} 117 | if (glErrorQueue = nil) then 118 | begin 119 | Result:=GL_NO_ERROR; 120 | Exit; 121 | end; 122 | 123 | Result:=glUpperError^; 124 | {$R+} 125 | end; 126 | 127 | function glGetCountErrors(): Integer; 128 | begin 129 | {$R-} 130 | if (glErrorQueue = nil) then 131 | begin 132 | Result:=0; 133 | Exit; 134 | end; 135 | 136 | Result:=((DWORD(glUpperError) - DWORD(glErrorQueue)) div SizeOf(GLenum)) + 1; 137 | {$R+} 138 | end; 139 | 140 | function glGetStrErrors(const ErrStrSeparator: Char): String; 141 | var 142 | CurrPtr: PGLenum; 143 | tmpStr: String; 144 | begin 145 | {$R-} 146 | if (glErrorQueue = nil) then 147 | begin 148 | Result:=''; 149 | Exit; 150 | end; 151 | 152 | // 1. Get string length 153 | Result:=''; 154 | CurrPtr:=glUpperError; 155 | while (CurrPtr <> glErrorQueue) do 156 | begin 157 | case (CurrPtr^) of 158 | GL_NO_ERROR: Result:=Result + STR_NO_ERROR; 159 | GL_INVALID_ENUM: Result:=Result + STR_INVALID_ENUM; 160 | GL_INVALID_VALUE: Result:=Result + STR_INVALID_VALUE; 161 | GL_INVALID_OPERATION: Result:=Result + STR_INVALID_OPERATION; 162 | GL_STACK_OVERFLOW: Result:=Result + STR_STACK_OVERFLOW; 163 | GL_STACK_UNDERFLOW: Result:=Result + STR_STACK_UNDERFLOW; 164 | GL_OUT_OF_MEMORY: Result:=Result + STR_OUT_OF_MEMORY; 165 | GL_INVALID_FRAMEBUFFER_OPERATION: Result:=Result + STR_INVALID_FRAMEBUFFER_OPERATION; 166 | GL_CONTEXT_LOST: Result:=Result + STR_CONTEXT_LOST; 167 | GL_TABLE_TOO_LARGE: Result:=Result + STR_TABLE_TOO_LARGE; 168 | else 169 | Str(CurrPtr^, tmpStr); 170 | Result:=Result + STR_UNKNOW_ + tmpStr; 171 | end; 172 | Result:=Result + ErrStrSeparator; 173 | Dec(CurrPtr); 174 | end; 175 | // 176 | case (CurrPtr^) of 177 | GL_NO_ERROR: Result:=Result + STR_NO_ERROR; 178 | GL_INVALID_ENUM: Result:=Result + STR_INVALID_ENUM; 179 | GL_INVALID_VALUE: Result:=Result + STR_INVALID_VALUE; 180 | GL_INVALID_OPERATION: Result:=Result + STR_INVALID_OPERATION; 181 | GL_STACK_OVERFLOW: Result:=Result + STR_STACK_OVERFLOW; 182 | GL_STACK_UNDERFLOW: Result:=Result + STR_STACK_UNDERFLOW; 183 | GL_OUT_OF_MEMORY: Result:=Result + STR_OUT_OF_MEMORY; 184 | GL_INVALID_FRAMEBUFFER_OPERATION: Result:=Result + STR_INVALID_FRAMEBUFFER_OPERATION; 185 | GL_CONTEXT_LOST: Result:=Result + STR_CONTEXT_LOST; 186 | GL_TABLE_TOO_LARGE: Result:=Result + STR_TABLE_TOO_LARGE; 187 | else 188 | Str(CurrPtr^, tmpStr); 189 | Result:=Result + STR_UNKNOW_ + tmpStr; 190 | end; 191 | {$R+} 192 | end; 193 | 194 | procedure glShowErrorsMessageBox(); 195 | begin 196 | {$R-} 197 | glScanErrors(); 198 | MessageBox(0, PAnsiChar(glGetStrErrors(#$0A)), 199 | 'OpenGL Error Manager', MB_OK or MB_ICONERROR or MB_TASKMODAL); 200 | glClearErrorQueue(); 201 | {$R-} 202 | end; 203 | 204 | 205 | initialization 206 | {$R-} 207 | glErrorQueue:=VirtualAlloc(nil, 64*1024, MEM_RESERVE or MEM_COMMIT, PAGE_READWRITE); 208 | if (glErrorQueue <> nil) then 209 | begin 210 | glClearErrorQueue(); 211 | // 212 | glUpperLimit:=glErrorQueue; 213 | Inc(glUpperLimit, (64*1024) div SizeOf(GLenum)); 214 | end; 215 | {$R+} 216 | 217 | finalization 218 | {$R-} 219 | glClearErrorQueue(); 220 | if (glErrorQueue <> nil) then VirtualFree(glErrorQueue, 0, MEM_RELEASE); 221 | {$R+} 222 | 223 | end. 224 | -------------------------------------------------------------------------------- /Modules/UnitOpenGLFPSCamera.pas: -------------------------------------------------------------------------------- 1 | unit UnitOpenGLFPSCamera; 2 | 3 | // Copyright (c) 2019-2025 Sergey-KoRJiK, Belarus 4 | 5 | // First Person View OpenGL Camera class 6 | // All angle values in radians 7 | 8 | interface 9 | 10 | uses 11 | SysUtils, 12 | Windows, 13 | Classes, 14 | OpenGL, 15 | UnitUserTypes, 16 | UnitVec; 17 | 18 | const 19 | CameraPolarAngleMax: GLfloat = Pi*0.5; 20 | CameraPolarAngleMin: GLfloat = -Pi*0.5; 21 | AngleToRadian: GLfloat = Pi/180.0; 22 | RadianToAngle: GLfloat = 180.0/Pi; 23 | Pi360: GLfloat = Pi/360.0; 24 | 25 | const 26 | FieldOfViewMin: GLfloat = 45.0; 27 | FieldOfViewMax: GLfloat = 120.0; 28 | zFarMin: GLfloat = 2.0; 29 | zNear: GLfloat = 1.0; 30 | 31 | const 32 | // Camera see at that pos to Axis origin. 33 | DefaultCameraPos: tVec3f = (X: 30.0; Y: 30.0; Z: 30.0); 34 | DefaultCameraPolarAngle: GLfloat = -0.523599; 35 | DefaultCameraAzimutalAngle: GLfloat = 0.785398; 36 | 37 | type CFirtsPersonViewCamera = class 38 | private 39 | CModelMat4f: tMat4f; 40 | CProjMat4f: tMat4f; 41 | 42 | CSizeX, CSizeY, CAspectRation: GLfloat; 43 | CFieldOfView, CZFar: GLfloat; 44 | CWidth, CHeight: GLint; 45 | // prefix N for vectors mean Normalized 46 | // (CNDir, CNSide, CNUp) is always orthogonal basis 47 | CPos, CNDir, CNUp, CNSide: tVec3f; 48 | CAlpha, CBetta: GLfloat; 49 | // Alpha angle do rotate around CNup and change CNDir and CNSide 50 | // Betta angle do rotate around CNSide and change CNup and CNDir 51 | 52 | FSin, FCos, FCosDivAspect: GLfloat; 53 | 54 | procedure WrapCAlpha(); 55 | procedure ClampPolarAngle(); 56 | procedure SetViewPosition(const Pos: tVec3f); 57 | procedure RebuildModelView(); 58 | procedure UpdateModelViewTranslate(); 59 | public 60 | property AzimutalAngle: GLfloat read CAlpha; // [radian] 61 | property PolarAngle: GLfloat read CBetta; // [radian] 62 | property ViewPosition: tVec3f read CPos write SetViewPosition; 63 | property ViewDirection: tVec3f read CNDir; 64 | property UpVector: tVec3f read CNUp; 65 | property SideVector: tVec3f read CNSide; 66 | property DistToAxis: GLfloat read CModelMat4f[14]; // camera distance to Axis origin 67 | // Camera CNDir with DistToAxis give Camera Plane equation 68 | 69 | property FieldOfView: GLfloat read CFieldOfView; 70 | property ScreenWidth: GLint read CWidth; 71 | property ScreenHeight: GLint read CHeight; 72 | property zFar: GLfloat read CZFar; 73 | 74 | constructor CreateNewCamera(const Pos: tVec3f; const PolarAngle, AzimutalAngle: GLfloat); 75 | destructor DeleteCamera(); 76 | 77 | procedure ResetCamera(const Pos: tVec3f; const PolarAngle, AzimutalAngle: GLfloat); 78 | procedure StepForward(const Dist: GLfloat); 79 | procedure StepBackward(const Dist: GLfloat); 80 | procedure StepLeft(const Dist: GLfloat); 81 | procedure StepRight(const Dist: GLfloat); 82 | 83 | procedure UpdateViewDirectionByMouseX(const AzimutalOffset: GLfloat); // [radian] 84 | procedure UpdateViewDirectionByMouseY(const PolarOffset: GLfloat); // [radian] 85 | 86 | // Fideld Of View in degrees. 87 | procedure SetProjMatrix(const ScreenWidth, ScreenHeight: GLint; FieldOfView, zFar: GLfloat); 88 | 89 | procedure glViewPortUpdate(); // set to OpenGL viewport 90 | procedure CopyModelMatrix(const dstMat4x4: PMat4f); 91 | procedure CopyProjectMatrix(const dstMat4x4: PMat4f); 92 | procedure CopyModelProjectMatrix(const dstMat4x4: PMat4f); 93 | 94 | // Get mouse ray by click on screen. 95 | // Mouse pos in windows coordinates, X from 0 to Width, Y from 0 to Height; 96 | // Mouse ray start at camera pos, ray dir is normalized. 97 | procedure GetTraceLineByMouseClick(const MousePosWinCoord: TPoint; const Ray: PRay); 98 | end; 99 | 100 | 101 | procedure PointToScreen(const Src, Dst: PVec3f; const Mat: PMat4f); 102 | 103 | 104 | implementation 105 | 106 | 107 | procedure PointToScreen(const Src, Dst: PVec3f; const Mat: PMat4f); 108 | var 109 | i: Integer; 110 | begin 111 | {$R-} 112 | Dst.x:=Mat[3*4+0]; 113 | Dst.y:=Mat[3*4+1]; 114 | Dst.z:=Mat[3*4+2]; 115 | for i:=0 to 2 do 116 | begin 117 | Dst.x:=Dst.x + Src.x*Mat[i*4+0]; 118 | Dst.y:=Dst.y + Src.y*Mat[i*4+1]; 119 | Dst.z:=Dst.z + Src.z*Mat[i*4+2]; 120 | end; 121 | Dst.x:=Dst.x/Dst.z; 122 | Dst.y:=Dst.y/Dst.z; 123 | {$R+} 124 | end; 125 | 126 | 127 | constructor CFirtsPersonViewCamera.CreateNewCamera(const Pos: tVec3f; 128 | const PolarAngle, AzimutalAngle: GLfloat); 129 | begin 130 | {$R-} 131 | inherited; 132 | 133 | Self.CNSide.z:=0.0; 134 | Self.CModelMat4f:=IdentityMat4f; 135 | Self.ResetCamera(Pos, PolarAngle, AzimutalAngle); 136 | 137 | Self.CProjMat4f:=ZerosMat4f; 138 | Self.CProjMat4f[11]:=-1.0; 139 | Self.SetProjMatrix(-1, -1, -1, -1); 140 | 141 | Self.FSin:=0.0; 142 | Self.FCos:=0.0; 143 | Self.FCosDivAspect:=0.0; 144 | {$R+} 145 | end; 146 | 147 | destructor CFirtsPersonViewCamera.DeleteCamera(); 148 | begin 149 | {$R-} 150 | inherited; 151 | {$R+} 152 | end; 153 | 154 | 155 | procedure CFirtsPersonViewCamera.WrapCAlpha(); 156 | asm 157 | {$R-} 158 | // check (abs(Angle) < 2*pi) 159 | push ebx 160 | mov ebx, CFirtsPersonViewCamera[EAX].CAlpha 161 | shl ebx, 1 162 | shr ebx, 1 163 | cmp ebx, $40C90FDB // 2*pi as dword is $40C90FDB 164 | pop ebx 165 | jl @@NoWrap // jump to exit if (abs(Angle) < 2*pi) 166 | 167 | // stack is null; 168 | fldpi 169 | // st0 = pi; 170 | fld st(0) 171 | // st0 = pi; st1 = pi; 172 | faddp st(1), st(0) 173 | // st0 = 2*pi; 174 | fld CFirtsPersonViewCamera[EAX].CAlpha 175 | // st0 = Angle, st1 = 2*pi; 176 | fprem1 // returns the remainder of division without rounding 177 | // st0 = Angle fmod 2*pi; st1 = 2*pi; 178 | fstp CFirtsPersonViewCamera[EAX].CAlpha 179 | // st0 = 2*pi; 180 | fstp st(0) 181 | // stack is null; 182 | 183 | @@NoWrap: 184 | {$R+} 185 | end; 186 | 187 | procedure CFirtsPersonViewCamera.ClampPolarAngle(); 188 | asm 189 | {$R-} 190 | push ebx 191 | push edx 192 | mov ebx, CFirtsPersonViewCamera[EAX].CBetta 193 | mov edx, ebx 194 | shl ebx, 1 195 | shr ebx, 1 196 | cmp ebx, $3FC90FDB // 0.5*Pi as dword is $3FC90FDB; -0.5*Pi = $BFC90FDB 197 | jle @@NoClamp // jump to exit if (abs(Angle) <= 0.5*pi) 198 | 199 | cmp edx, ebx 200 | jne @@ClampToMin // if ebx <> edx -> CBetta is neg, need clamp to Min 201 | // ebx = edx -> CBetta is positive, need clamp to Max 202 | mov edx, $3FC90FDB 203 | mov CFirtsPersonViewCamera[EAX].CBetta, edx 204 | jmp @@NoClamp // go to exit 205 | 206 | @@ClampToMin: 207 | mov edx, $BFC90FDB 208 | mov CFirtsPersonViewCamera[EAX].CBetta, edx 209 | 210 | @@NoClamp: 211 | pop edx 212 | pop ebx 213 | {$R+} 214 | end; 215 | 216 | procedure CFirtsPersonViewCamera.SetViewPosition(const Pos: tVec3f); 217 | begin 218 | {$R-} 219 | Self.CPos:=Pos; 220 | 221 | Self.UpdateModelViewTranslate(); 222 | {$R+} 223 | end; 224 | 225 | procedure CFirtsPersonViewCamera.RebuildModelView(); 226 | begin 227 | {$R-} 228 | Self.CModelMat4f[0]:=Self.CNSide.x; 229 | Self.CModelMat4f[4]:=Self.CNSide.y; 230 | 231 | Self.CModelMat4f[1]:=Self.CNUp.x; 232 | Self.CModelMat4f[5]:=Self.CNUp.y; 233 | Self.CModelMat4f[9]:=Self.CNUp.z; 234 | 235 | Self.CModelMat4f[2]:=-Self.CNDir.x; 236 | Self.CModelMat4f[6]:=-Self.CNDir.y; 237 | Self.CModelMat4f[10]:=-Self.CNDir.z; 238 | 239 | Self.CModelMat4f[12]:=-Self.CPos.x*Self.CNSide.x - Self.CPos.y*Self.CNSide.y; 240 | Self.CModelMat4f[13]:=-Self.CPos.x*Self.CNUp.x - Self.CPos.y*Self.CNUp.y 241 | - Self.CPos.z*Self.CNUp.z; 242 | Self.CModelMat4f[14]:=Self.CPos.x*Self.CNDir.x + Self.CPos.y*Self.CNDir.y + 243 | Self.CPos.z*Self.CNDir.z; 244 | {$R+} 245 | end; 246 | 247 | procedure CFirtsPersonViewCamera.UpdateModelViewTranslate(); 248 | begin 249 | {$R-} 250 | Self.CModelMat4f[12]:=-Self.CPos.x*Self.CNSide.x - Self.CPos.y*Self.CNSide.y; 251 | Self.CModelMat4f[13]:=-Self.CPos.x*Self.CNUp.x - Self.CPos.y*Self.CNUp.y 252 | - Self.CPos.z*Self.CNUp.z; 253 | Self.CModelMat4f[14]:=Self.CPos.x*Self.CNDir.x + Self.CPos.y*Self.CNDir.y + 254 | Self.CPos.z*Self.CNDir.z; 255 | {$R+} 256 | end; 257 | 258 | procedure CFirtsPersonViewCamera.ResetCamera(const Pos: tVec3f; 259 | const PolarAngle, AzimutalAngle: GLfloat); 260 | begin 261 | {$R-} 262 | Self.CPos:=Pos; 263 | 264 | Self.CAlpha:=AzimutalAngle; 265 | Self.WrapCAlpha(); 266 | // 267 | Self.CNSide.x:=-Cos(Self.CAlpha); 268 | Self.CNSide.y:=Sin(Self.CAlpha); 269 | 270 | Self.CBetta:=PolarAngle; 271 | Self.ClampPolarAngle(); 272 | // 273 | Self.CNDir.z:=Sin(Self.CBetta); 274 | Self.CNUp.z:=Cos(Self.CBetta); 275 | // 276 | Self.CNDir.x:=-Self.CNSide.y*Self.CNUp.z; 277 | Self.CNDir.y:=Self.CNSide.x*Self.CNUp.z; 278 | // 279 | Self.CNUp.x:=Self.CNSide.y*Self.CNDir.z; 280 | Self.CNUp.y:=-Self.CNSide.x*Self.CNDir.z; 281 | 282 | Self.RebuildModelView(); 283 | {$R+} 284 | end; 285 | 286 | procedure CFirtsPersonViewCamera.StepForward(const Dist: GLfloat); 287 | begin 288 | {$R-} 289 | Self.CPos.x:=Self.CPos.x + Self.CNDir.x*Dist; 290 | Self.CPos.y:=Self.CPos.y + Self.CNDir.y*Dist; 291 | Self.CPos.z:=Self.CPos.z + Self.CNDir.z*Dist; 292 | 293 | Self.UpdateModelViewTranslate(); 294 | {$R+} 295 | end; 296 | 297 | procedure CFirtsPersonViewCamera.StepBackward(const Dist: GLfloat); 298 | begin 299 | {$R-} 300 | Self.CPos.x:=Self.CPos.x - Self.CNDir.x*Dist; 301 | Self.CPos.y:=Self.CPos.y - Self.CNDir.y*Dist; 302 | Self.CPos.z:=Self.CPos.z - Self.CNDir.z*Dist; 303 | 304 | Self.UpdateModelViewTranslate(); 305 | {$R+} 306 | end; 307 | 308 | procedure CFirtsPersonViewCamera.StepLeft(const Dist: GLfloat); 309 | begin 310 | {$R-} 311 | Self.CPos.x:=Self.CPos.x - Self.CNSide.x*Dist; 312 | Self.CPos.y:=Self.CPos.y - Self.CNSide.y*Dist; 313 | 314 | Self.UpdateModelViewTranslate(); 315 | {$R+} 316 | end; 317 | 318 | procedure CFirtsPersonViewCamera.StepRight(const Dist: GLfloat); 319 | begin 320 | {$R-} 321 | Self.CPos.x:=Self.CPos.x + Self.CNSide.x*Dist; 322 | Self.CPos.y:=Self.CPos.y + Self.CNSide.y*Dist; 323 | 324 | Self.UpdateModelViewTranslate(); 325 | {$R+} 326 | end; 327 | 328 | 329 | procedure CFirtsPersonViewCamera.UpdateViewDirectionByMouseX(const AzimutalOffset: GLfloat); 330 | begin 331 | {$R-} 332 | Self.CAlpha:=Self.CAlpha + AzimutalOffset; 333 | Self.WrapCAlpha(); 334 | // 335 | Self.CNSide.x:=-Cos(Self.CAlpha); 336 | Self.CNSide.y:=Sin(Self.CAlpha); 337 | 338 | Self.CNDir.x:=-Self.CNSide.y*Self.CNUp.z; 339 | Self.CNDir.y:=Self.CNSide.x*Self.CNUp.z; 340 | // 341 | Self.CNUp.x:=Self.CNSide.y*Self.CNDir.z; 342 | Self.CNUp.y:=-Self.CNSide.x*Self.CNDir.z; 343 | 344 | Self.RebuildModelView(); 345 | {$R+} 346 | end; 347 | 348 | procedure CFirtsPersonViewCamera.UpdateViewDirectionByMouseY(const PolarOffset: GLfloat); 349 | begin 350 | {$R-} 351 | Self.CBetta:=Self.CBetta - PolarOffset; 352 | Self.ClampPolarAngle(); 353 | // 354 | Self.CNDir.z:=Sin(Self.CBetta); 355 | Self.CNUp.z:=Cos(Self.CBetta); 356 | // 357 | Self.CNDir.x:=-Self.CNSide.y*Self.CNUp.z; 358 | Self.CNDir.y:=Self.CNSide.x*Self.CNUp.z; 359 | // 360 | Self.CNUp.x:=Self.CNSide.y*Self.CNDir.z; 361 | Self.CNUp.y:=-Self.CNSide.x*Self.CNDir.z; 362 | 363 | Self.RebuildModelView(); 364 | {$R+} 365 | end; 366 | 367 | procedure CFirtsPersonViewCamera.SetProjMatrix(const ScreenWidth, ScreenHeight: GLint; 368 | FieldOfView, zFar: GLfloat); 369 | begin 370 | {$R-} 371 | Self.CFieldOfView:=FieldOfView; 372 | if (Self.CFieldOfView < FieldOfViewMin) then Self.CFieldOfView:=FieldOfViewMin; 373 | if (Self.CFieldOfView > FieldOfViewMax) then Self.CFieldOfView:=FieldOfViewMax; 374 | 375 | Self.CWidth:=ScreenWidth; 376 | Self.CHeight:=ScreenHeight; 377 | if (Self.CWidth <= 1) then Self.CWidth:=1; 378 | if (Self.CHeight <= 1) then Self.CHeight:=1; 379 | Self.CAspectRation:=Self.CWidth/Self.CHeight; 380 | 381 | Self.CZFar:=zFar; 382 | if (Self.CZFar < zFarMin) then Self.CZFar:=zFarMin; 383 | 384 | Self.FSin:=Sin(Self.CFieldOfView*Pi360); 385 | Self.FCos:=Cos(Self.CFieldOfView*Pi360); 386 | Self.FCosDivAspect:=Self.FCos/Self.CAspectRation; 387 | 388 | Self.CSizeY:=Self.FSin/Self.FCos; 389 | Self.CSizeX:=Self.CAspectRation*Self.CSizeY; 390 | 391 | Self.CProjMat4f[0]:=1.0/Self.CSizeX; 392 | Self.CProjMat4f[5]:=1.0/Self.CSizeY; 393 | Self.CProjMat4f[10]:=(Self.CZFar + 1.0)/(1.0 - Self.CZFar); 394 | Self.CProjMat4f[14]:=2.0*Self.CZFar/(1.0 - Self.CZFar); 395 | {$R+} 396 | end; 397 | 398 | procedure CFirtsPersonViewCamera.glViewPortUpdate(); 399 | begin 400 | {$R-} 401 | glViewport(0, 0, Self.CWidth, Self.CHeight); 402 | {$R+} 403 | end; 404 | 405 | 406 | procedure CFirtsPersonViewCamera.CopyModelMatrix(const dstMat4x4: PMat4f); 407 | begin 408 | {$R-} 409 | dstMat4x4[ 0]:=Self.CModelMat4f[ 0]; 410 | dstMat4x4[ 1]:=Self.CModelMat4f[ 1]; 411 | dstMat4x4[ 2]:=Self.CModelMat4f[ 2]; 412 | dstMat4x4[ 3]:=Self.CModelMat4f[ 3]; 413 | // 414 | dstMat4x4[ 4]:=Self.CModelMat4f[ 4]; 415 | dstMat4x4[ 5]:=Self.CModelMat4f[ 5]; 416 | dstMat4x4[ 6]:=Self.CModelMat4f[ 6]; 417 | dstMat4x4[ 7]:=Self.CModelMat4f[ 7]; 418 | // 419 | dstMat4x4[ 8]:=Self.CModelMat4f[ 8]; 420 | dstMat4x4[ 9]:=Self.CModelMat4f[ 9]; 421 | dstMat4x4[10]:=Self.CModelMat4f[10]; 422 | dstMat4x4[11]:=Self.CModelMat4f[11]; 423 | // 424 | dstMat4x4[12]:=Self.CModelMat4f[12]; 425 | dstMat4x4[13]:=Self.CModelMat4f[13]; 426 | dstMat4x4[14]:=Self.CModelMat4f[14]; 427 | dstMat4x4[15]:=Self.CModelMat4f[15]; 428 | {$R+} 429 | end; 430 | 431 | procedure CFirtsPersonViewCamera.CopyProjectMatrix(const dstMat4x4: PMat4f); 432 | begin 433 | {$R-} 434 | dstMat4x4[ 0]:=Self.CProjMat4f[ 0]; 435 | dstMat4x4[ 1]:=Self.CProjMat4f[ 1]; 436 | dstMat4x4[ 2]:=Self.CProjMat4f[ 2]; 437 | dstMat4x4[ 3]:=Self.CProjMat4f[ 3]; 438 | // 439 | dstMat4x4[ 4]:=Self.CProjMat4f[ 4]; 440 | dstMat4x4[ 5]:=Self.CProjMat4f[ 5]; 441 | dstMat4x4[ 6]:=Self.CProjMat4f[ 6]; 442 | dstMat4x4[ 7]:=Self.CProjMat4f[ 7]; 443 | // 444 | dstMat4x4[ 8]:=Self.CProjMat4f[ 8]; 445 | dstMat4x4[ 9]:=Self.CProjMat4f[ 9]; 446 | dstMat4x4[10]:=Self.CProjMat4f[10]; 447 | dstMat4x4[11]:=Self.CProjMat4f[11]; 448 | // 449 | dstMat4x4[12]:=Self.CProjMat4f[12]; 450 | dstMat4x4[13]:=Self.CProjMat4f[13]; 451 | dstMat4x4[14]:=Self.CProjMat4f[14]; 452 | dstMat4x4[15]:=Self.CProjMat4f[15]; 453 | {$R+} 454 | end; 455 | 456 | procedure CFirtsPersonViewCamera.CopyModelProjectMatrix(const dstMat4x4: PMat4f); 457 | var 458 | i, j: Integer; 459 | begin 460 | {$R-} 461 | for i:=0 to 3 do 462 | for j:=0 to 3 do 463 | begin 464 | dstMat4x4[4*i + j]:= 465 | Self.CModelMat4f[4*i + 0]*Self.CProjMat4f[0*4 + j] + 466 | Self.CModelMat4f[4*i + 1]*Self.CProjMat4f[1*4 + j] + 467 | Self.CModelMat4f[4*i + 2]*Self.CProjMat4f[2*4 + j] + 468 | Self.CModelMat4f[4*i + 3]*Self.CProjMat4f[3*4 + j]; 469 | end; 470 | {$R+} 471 | end; 472 | 473 | procedure CFirtsPersonViewCamera.GetTraceLineByMouseClick(const MousePosWinCoord: TPoint; 474 | const Ray: PRay); 475 | var 476 | x, y: GLfloat; 477 | begin 478 | {$R-} 479 | x:=Self.CSizeX*(2.0*MousePosWinCoord.X/Self.CWidth - 1.0); 480 | y:=Self.CSizeY*(1.0 - 2.0*MousePosWinCoord.Y/Self.CHeight); 481 | 482 | Ray.Dir.x:=x*Self.CNSide.x + y*Self.CNUp.x + Self.CNDir.x; 483 | Ray.Dir.y:=x*Self.CNSide.y + y*Self.CNUp.y + Self.CNDir.y; 484 | Ray.Dir.z:=y*Self.CNUp.z + Self.CNDir.z; 485 | Ray.Dir.w:=0; 486 | NormalizeVec3f(@Ray.Dir); 487 | 488 | Ray.Start.Vec3f:=Self.CPos; 489 | Ray.Start.w:=0; 490 | {$R+} 491 | end; 492 | 493 | 494 | end. 495 | -------------------------------------------------------------------------------- /Modules/UnitQueryPerformanceProfiler.pas: -------------------------------------------------------------------------------- 1 | unit UnitQueryPerformanceProfiler; 2 | 3 | // Copyright (c) 2022 Sergey-KoRJiK, Belarus 4 | // github.com/Sergey-KoRJiK 5 | 6 | interface 7 | 8 | uses 9 | SysUtils, 10 | Windows, 11 | Classes; 12 | 13 | type 14 | tProfileTag = array[0..15] of AnsiChar; // Null-terminated Ansi-string 15 | PProfileTag = ^tProfileTag; 16 | AProfileTag = array of tProfileTag; 17 | 18 | type tProfileSample = packed record 19 | StartPoint, EndPoint: Int64; 20 | TagName: tProfileTag; 21 | end; // 32 Bytes 22 | type PProfileSample = ^tProfileSample; 23 | type AProfileSample = array of tProfileSample; 24 | 25 | const 26 | MAX_PROFILE_STACK_SIZE = 1024; 27 | PROFILE_TAGNAME_NULL: tProfileTag = ( 28 | #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0 29 | ); 30 | PROFILE_SAMPLE_NULL: tProfileSample = ( 31 | StartPoint: 0; EndPoint: 0; 32 | TagName: (#0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0); 33 | ); 34 | PROFILE_FLAG_CLEAR: Byte = 0; 35 | PROFILE_FLAG_NAMED: Byte = 1 shl 0; 36 | PROFILE_FLAG_USING: Byte = 1 shl 1; 37 | PROFILE_FLAG_CLOSE: Byte = 1 shl 2; 38 | 39 | type CQueryPerformanceProfiler = class 40 | private 41 | Frequency: Int64; 42 | StackIndex: Integer; 43 | // 44 | ProfileFlags: array[0..(MAX_PROFILE_STACK_SIZE - 1)] of Byte; 45 | ProfileStack: array[0..(MAX_PROFILE_STACK_SIZE - 1)] of tProfileSample; 46 | // 47 | public 48 | property CurrentStackId: Integer read StackIndex; 49 | // 50 | constructor CreateProfiler(); 51 | destructor DeleteProfiler(); 52 | // 53 | procedure Clear(); 54 | function FindProfileTag(const ProfileTag: PProfileTag): Integer; 55 | function ReserveProfileTag(const TagName: String): Integer; 56 | end; 57 | 58 | 59 | function StrToProfileTag(const Src: String; const Dst: PProfileTag): Integer; 60 | 61 | 62 | implementation 63 | 64 | 65 | function StrToProfileTag(const Src: String; const Dst: PProfileTag): Integer; 66 | var 67 | i, n: Integer; 68 | begin 69 | {$R-} 70 | n:=Length(Src); 71 | if (n > 15) then n:=15; 72 | for i:=0 to (n - 1) do 73 | begin 74 | Dst[i]:=Src[i + 1]; 75 | end; 76 | Dst[n]:=#0; 77 | Result:=n; 78 | {$R+} 79 | end; 80 | 81 | 82 | constructor CQueryPerformanceProfiler.CreateProfiler(); 83 | begin 84 | {$R-} 85 | inherited; 86 | 87 | QueryPerformanceFrequency(Self.Frequency); 88 | Self.Clear(); 89 | {$R+} 90 | end; 91 | 92 | destructor CQueryPerformanceProfiler.DeleteProfiler(); 93 | begin 94 | {$R-} 95 | inherited; 96 | {$R+} 97 | end; 98 | 99 | procedure CQueryPerformanceProfiler.Clear(); 100 | var 101 | i: Integer; 102 | begin 103 | {$R-} 104 | Self.StackIndex:=-1; 105 | for i:=0 to (MAX_PROFILE_STACK_SIZE - 1) do 106 | begin 107 | ProfileFlags[i]:=PROFILE_FLAG_CLEAR; 108 | ProfileStack[i]:=PROFILE_SAMPLE_NULL; 109 | end; 110 | {$R+} 111 | end; 112 | 113 | function CQueryPerformanceProfiler.FindProfileTag( 114 | const ProfileTag: PProfileTag): Integer; 115 | {var 116 | i: Integer; //} 117 | begin 118 | {$R-} 119 | 120 | Result:=-1; 121 | {$R+} 122 | end; 123 | 124 | function CQueryPerformanceProfiler.ReserveProfileTag( 125 | const TagName: String): Integer; 126 | var 127 | tmpTagName: tProfileTag; 128 | begin 129 | {$R-} 130 | Result:=-1; 131 | 132 | if (Self.StackIndex >= (MAX_PROFILE_STACK_SIZE - 1)) then Exit; 133 | if (StrToProfileTag(TagName, @tmpTagName) = 0) then Exit; 134 | {$R+} 135 | end; 136 | 137 | end. 138 | -------------------------------------------------------------------------------- /Modules/UnitQueryPerformanceTimer.pas: -------------------------------------------------------------------------------- 1 | unit UnitQueryPerformanceTimer; 2 | 3 | // Copyright (c) 2020 Sergey-KoRJiK, Belarus 4 | // github.com/Sergey-KoRJiK 5 | 6 | interface 7 | 8 | uses 9 | SysUtils, 10 | Windows, 11 | Classes; 12 | 13 | type CQueryPerformanceTimer = class 14 | private 15 | Frequency: Int64; 16 | StartPoint, EndPoint: Int64; 17 | MeashuredInterval: Int64; 18 | TimerWorkState: Boolean; 19 | public 20 | property IsTimerWork: Boolean read TimerWorkState; 21 | constructor CreateTimer(); 22 | destructor DeleteTimer(); 23 | procedure TimerStart(); 24 | procedure TimerUpdate(); 25 | procedure TimerStop(); 26 | function GetStringSecInterval(): String; 27 | function GetStringMsInterval(): String; 28 | function GetStringMcsInterval(): String; 29 | function GetStringFPS(): String; 30 | function GetSecInterval(): Extended; 31 | function GetMsInterval(): Extended; 32 | function GetMcsInterval(): Extended; 33 | end; 34 | 35 | implementation 36 | 37 | const 38 | SecToMsScale: SmallInt = 1000; 39 | SecToMcsScale: Single = 1E+6; 40 | 41 | constructor CQueryPerformanceTimer.CreateTimer(); 42 | begin 43 | {$R-} 44 | inherited; 45 | 46 | QueryPerformanceFrequency(Self.Frequency); 47 | Self.StartPoint:=0; 48 | Self.EndPoint:=0; 49 | Self.TimerWorkState:=False; 50 | {$R+} 51 | end; 52 | 53 | procedure CQueryPerformanceTimer.TimerStart(); 54 | begin 55 | {$R-} 56 | Self.TimerWorkState:=True; 57 | QueryPerformanceCounter(Self.StartPoint); 58 | {$R+} 59 | end; 60 | 61 | procedure CQueryPerformanceTimer.TimerUpdate(); 62 | begin 63 | {$R-} 64 | if (Self.TimerWorkState) then 65 | begin 66 | QueryPerformanceCounter(Self.EndPoint); 67 | Self.MeashuredInterval:=Self.EndPoint - Self.StartPoint; 68 | end; 69 | {$R+} 70 | end; 71 | 72 | procedure CQueryPerformanceTimer.TimerStop(); 73 | begin 74 | {$R-} 75 | QueryPerformanceCounter(Self.EndPoint); 76 | Self.MeashuredInterval:=Self.EndPoint - Self.StartPoint; 77 | Self.TimerWorkState:=False; 78 | {$R+} 79 | end; 80 | 81 | function CQueryPerformanceTimer.GetStringSecInterval(): String; 82 | begin 83 | {$R-} 84 | Result:=FloatToStrF(Self.MeashuredInterval/Self.Frequency, ffFixed, 6, 4); 85 | {$R+} 86 | end; 87 | 88 | function CQueryPerformanceTimer.GetStringMsInterval(): String; 89 | begin 90 | {$R-} 91 | Result:=FloatToStrF(SecToMsScale*(Self.MeashuredInterval/Self.Frequency), ffFixed, 6, 4); 92 | {$R+} 93 | end; 94 | 95 | function CQueryPerformanceTimer.GetStringMcsInterval(): String; 96 | begin 97 | {$R-} 98 | Result:=FloatToStrF(SecToMcsScale*(Self.MeashuredInterval/Self.Frequency), ffFixed, 6, 4); 99 | {$R+} 100 | end; 101 | 102 | function CQueryPerformanceTimer.GetStringFPS(): String; 103 | begin 104 | {$R-} 105 | Result:=FloatToStrF(Self.Frequency/Self.MeashuredInterval, ffFixed, 6, 1); 106 | {$R+} 107 | end; 108 | 109 | function CQueryPerformanceTimer.GetSecInterval(): Extended; 110 | asm 111 | {$R-} 112 | //Result:=SecToMsScale*Self.MeashuredInterval/Self.Frequency; 113 | fild CQueryPerformanceTimer[EAX].MeashuredInterval 114 | fild CQueryPerformanceTimer[EAX].Frequency 115 | fdivp 116 | {$R+} 117 | end; 118 | 119 | function CQueryPerformanceTimer.GetMsInterval(): Extended; 120 | asm 121 | {$R-} 122 | //Result:=SecToMsScale*Self.MeashuredInterval/Self.Frequency; 123 | fild CQueryPerformanceTimer[EAX].MeashuredInterval 124 | fimul SecToMsScale 125 | fild CQueryPerformanceTimer[EAX].Frequency 126 | fdivp 127 | {$R+} 128 | end; 129 | 130 | function CQueryPerformanceTimer.GetMcsInterval(): Extended; 131 | asm 132 | {$R-} 133 | //Result:=SecToMsScale*Self.MeashuredInterval/Self.Frequency; 134 | fild CQueryPerformanceTimer[EAX].MeashuredInterval 135 | fmul SecToMcsScale 136 | fild CQueryPerformanceTimer[EAX].Frequency 137 | fdivp 138 | {$R+} 139 | end; 140 | 141 | destructor CQueryPerformanceTimer.DeleteTimer(); 142 | begin 143 | {$R-} 144 | inherited; 145 | {$R+} 146 | end; 147 | 148 | end. 149 | -------------------------------------------------------------------------------- /Modules/UnitRadianceHDR.pas: -------------------------------------------------------------------------------- 1 | unit UnitRadianceHDR; 2 | 3 | // Copyright (c) 2019 Sergey Smolovsky, Belarus 4 | 5 | // For import\export Lightmap BSP data to *.hdr Radiance RGBE image format 6 | // http://radsite.lbl.gov/radiance/refer/filefmts.pdf 7 | 8 | // This product includes Radiance software 9 | // (http://radsite.lbl.gov/) 10 | // developed by the Lawrence Berkeley National Laboratory 11 | // (http://www.lbl.gov/). 12 | 13 | interface 14 | 15 | uses 16 | SysUtils, 17 | Windows, 18 | Classes; 19 | 20 | 21 | const 22 | SignatureRad = '#?RADIANCE'; 23 | SignatureRadSize = Length(SignatureRad); 24 | HeaderFormatRad = 'FORMAT=32-bit_rle_rgbe'; 25 | HeaderFormatRadSize = Length(HeaderFormatRad); 26 | HeaderDelemiterRad: Byte = $0A; 27 | SpaceDelemiterRad: Byte = $20; 28 | EndOfHeaderRad: Word = $0A0A; 29 | // 30 | DirSize = 2; // Sign char + char 'X' or char 'Y' 31 | DirLeftRight = '+X'; 32 | DirRightLeft = '-X'; 33 | DirTopBottom = '-Y'; 34 | DirBottomTop = '+Y'; 35 | // 36 | MinHDRFileSize = 37; 37 | 38 | 39 | type eLoadStateHDR = ( 40 | hdrOK, 41 | hdrNoExists, 42 | hdrBadHeader, 43 | hdrBadFormat, 44 | hdrMinSize, 45 | hdrBadWidth, 46 | hdrBadHeight, 47 | hdrBadScanWidth, 48 | hdrBadScanHeight, 49 | hdrBadRLE 50 | ); 51 | 52 | type tHDREntry = record 53 | r, g, b: Byte; 54 | e: ShortInt; 55 | end; 56 | type PHDREntry = ^tHDREntry; 57 | type AHDREntry = array of tHDREntry; 58 | 59 | 60 | procedure WriteBytesRLE(const ByteFile: File; const lpData: PByte; const numbytes: Integer); 61 | procedure SaveToHDRFile(const FileName: String; const Width, Height: Integer; 62 | const Lightmaps: PHDREntry); 63 | function LoadFromHDRFile(const FileName: String; const Width, Height: Integer; 64 | const Lightmaps: PHDREntry): eLoadStateHDR; 65 | function ShowLoadHDRError(const hdrError: eLoadStateHDR): String; 66 | 67 | 68 | implementation 69 | 70 | 71 | type AByte = array of Byte; 72 | 73 | 74 | procedure WriteBytesRLE(const ByteFile: File; const lpData: PByte; const numbytes: Integer); 75 | const 76 | MinRunLength = 4; 77 | var 78 | cur, beg_run, run_count, old_run_count, nonrun_count: Integer; 79 | buf: array[0..1] of Byte; 80 | begin 81 | {$R-} 82 | cur:=0; 83 | while (cur < numbytes) do 84 | begin 85 | beg_run:=cur; 86 | 87 | // find next run of length at least 4 if one exists 88 | run_count:=0; 89 | old_run_count:=0; 90 | while ((run_count < MinRunLength) and (beg_run < numbytes)) do 91 | begin 92 | Inc(beg_run, run_count); 93 | old_run_count:=run_count; 94 | 95 | run_count:=1; 96 | while ( ((beg_run + run_count) < numbytes) and (run_count < 127) 97 | and (AByte(lpData)[beg_run] = AByte(lpData)[beg_run + run_count]) ) do 98 | begin 99 | Inc(run_count); 100 | end; 101 | end; 102 | 103 | // if data before next big run is a short run then write it as such 104 | if ( (old_run_count > 1) and (old_run_count = (beg_run - cur)) ) then 105 | begin 106 | // write short run 107 | buf[0]:=128 + old_run_count; 108 | buf[1]:=AByte(lpData)[cur]; 109 | BlockWrite(ByteFile, Pointer(@buf[0])^, 2); 110 | 111 | cur:=beg_run; 112 | end; 113 | 114 | // write out bytes until we reach the start of the next run 115 | while (cur < beg_run) do 116 | begin 117 | nonrun_count:=beg_run - cur; 118 | if (nonrun_count > 128) then nonrun_count:=128; 119 | 120 | buf[0]:=nonrun_count; 121 | BlockWrite(ByteFile, Pointer(@buf[0])^, 1); 122 | BlockWrite(ByteFile, Pointer(@(AByte(lpData)[cur]))^, nonrun_count); 123 | 124 | Inc(cur, nonrun_count); 125 | end; 126 | 127 | // write out next run if one was found 128 | if (run_count >= MinRunLength) then 129 | begin 130 | buf[0]:=128 + run_count; 131 | buf[1]:=AByte(lpData)[beg_run]; 132 | BlockWrite(ByteFile, Pointer(@buf[0])^, 2); 133 | 134 | Inc(cur, run_count); 135 | end; 136 | end; 137 | {$R+} 138 | end; 139 | 140 | procedure SaveToHDRFile(const FileName: String; const Width, Height: Integer; 141 | const Lightmaps: PHDREntry); 142 | var 143 | FileHDR: File; 144 | tmpStr: String; 145 | tmpPointer: PHDREntry; 146 | Buffer: AByte; 147 | RunLenHeader: array[0..3] of Byte; 148 | i, j, SqrSize: Integer; 149 | begin 150 | {$R-} 151 | AssignFile(FileHDR, FileName + '.hdr'); 152 | Rewrite(FileHDR, 1); 153 | 154 | // Write simple header with minimal information 155 | // 1. Write Signature "RADIANCE" 156 | BlockWrite(FileHDR, Pointer(@SignatureRad[1])^, SignatureRadSize); 157 | BlockWrite(FileHDR, Pointer(@HeaderDelemiterRad)^, 1); 158 | // 2. Write Format "RGBE with RLE" 159 | BlockWrite(FileHDR, Pointer(@HeaderFormatRad[1])^, HeaderFormatRadSize); 160 | BlockWrite(FileHDR, Pointer(@HeaderDelemiterRad)^, 1); 161 | // 3. Write null string - end of header 162 | BlockWrite(FileHDR, Pointer(@HeaderDelemiterRad)^, 1); 163 | 164 | // Next write image size info with standard write direction "left->right, top->bottom" 165 | // 1. Get direction format 166 | tmpStr:=DirTopBottom 167 | + ' ' + IntToStr(Height) 168 | + ' ' + DirLeftRight 169 | + ' ' + IntToStr(Width); 170 | // 2. Write direction format with end byte 171 | BlockWrite(FileHDR, Pointer(@tmpStr[1])^, Length(tmpStr)); 172 | BlockWrite(FileHDR, Pointer(@HeaderDelemiterRad)^, 1); 173 | 174 | // Final - write pixel data 175 | SqrSize:=Width*Height; 176 | tmpPointer:=Lightmaps; 177 | if ((Width < 8) or (Width > $7FFF)) then 178 | begin 179 | // run length encoding is not allowed so write flat 180 | if ((Width > 0) and (Height > 0)) then 181 | begin 182 | SetLength(Buffer, SizeOf(tHDREntry)*SqrSize); 183 | // Correct Lightmap RGBExp to Radiance RGBE 184 | for i:=0 to (SqrSize - 1) do 185 | begin 186 | Buffer[i*4]:=tmpPointer.r; 187 | Buffer[i*4 + 1]:=tmpPointer.g; 188 | Buffer[i*4 + 2]:=tmpPointer.b; 189 | Buffer[i*4 + 3]:=tmpPointer.e + 128; 190 | Inc(tmpPointer); 191 | end; 192 | BlockWrite(FileHDR, Pointer(@Buffer[0])^, SizeOf(tHDREntry)*SqrSize); 193 | end; 194 | end 195 | else 196 | begin 197 | // Create header for run-length 198 | // width write to file in Big-Endian 199 | RunLenHeader[0]:=2; 200 | RunLenHeader[1]:=2; 201 | RunLenHeader[2]:=Width shr 8; 202 | RunLenHeader[3]:=(Width and $FF); 203 | 204 | SetLength(Buffer, SizeOf(tHDREntry)*Width); 205 | for i:=0 to (Height - 1) do 206 | begin 207 | // Write Run-Length Header 208 | BlockWrite(FileHDR, Pointer(@RunLenHeader[0])^, 4); 209 | 210 | // Correct Lightmap RGBExp to Radiance RGBE 211 | for j:=0 to (Width - 1) do 212 | begin 213 | Buffer[j]:=tmpPointer.r; 214 | Buffer[j + Width]:=tmpPointer.g; 215 | Buffer[j + 2*Width]:=tmpPointer.b; 216 | Buffer[j + 3*Width]:=tmpPointer.e + 128; 217 | Inc(tmpPointer); 218 | end; 219 | 220 | // write out each of the four channels separately run length encoded 221 | // first red, then green, then blue, then exponent 222 | WriteBytesRLE(FileHDR, @Buffer[0], Width); 223 | WriteBytesRLE(FileHDR, @Buffer[Width], Width); 224 | WriteBytesRLE(FileHDR, @Buffer[2*Width], Width); 225 | WriteBytesRLE(FileHDR, @Buffer[3*Width], Width); 226 | end; 227 | end; 228 | SetLength(Buffer, 0); 229 | CloseFile(FileHDR); 230 | {$R+} 231 | end; 232 | 233 | function LoadFromHDRFile(const FileName: String; const Width, Height: Integer; 234 | const Lightmaps: PHDREntry): eLoadStateHDR; 235 | var 236 | FileHDR: File; 237 | tmpStr: String; 238 | tmpPointer: PHDREntry; 239 | Buffer: AByte; 240 | RunLenHeader: array[0..3] of Byte; 241 | i, j, SqrSize, SizeOfFile, Count, k: Integer; 242 | tmpList: TStringList; 243 | // 244 | BufWord: array[0..1] of Byte; 245 | ptr, ptr_end: PByte; 246 | begin 247 | {$R-} 248 | if (FileExists(FileName) = False) then 249 | begin 250 | Result:=hdrNoExists; 251 | Exit; 252 | end; 253 | 254 | AssignFile(FileHDR, FileName); 255 | Reset(FileHDR, 1); 256 | 257 | SizeOfFile:=FileSize(FileHDR); 258 | if (SizeOfFile < MinHDRFileSize) then 259 | begin 260 | CloseFile(FileHDR); 261 | Result:=hdrMinSize; 262 | Exit; 263 | end; 264 | 265 | // Scan Header 266 | SetLength(tmpStr, 2); 267 | i:=2; 268 | BlockRead(FileHDR, Pointer(@tmpStr[1])^, 2); 269 | while not( (tmpStr[i - 1] = Char(HeaderDelemiterRad)) 270 | and (tmpStr[i] = Char(HeaderDelemiterRad)) ) do 271 | begin 272 | Inc(i); 273 | SetLength(tmpStr, i); 274 | BlockRead(FileHDR, Pointer(@tmpStr[i])^, 1); 275 | 276 | if (EOF(FileHDR)) then 277 | begin 278 | CloseFile(FileHDR); 279 | Result:=hdrBadHeader; 280 | Exit; 281 | end; 282 | end; 283 | 284 | tmpList:=TStringList.Create(); 285 | // Check Format 286 | {tmpList.Delimiter:=Char(HeaderDelemiterRad); 287 | tmpList.DelimitedText:=tmpStr; 288 | if (tmpList.Find(HeaderFormatRad, i) = False) then 289 | begin 290 | CloseFile(FileHDR); 291 | tmpList.Destroy; 292 | Result:=hdrBadFormat; 293 | Exit; 294 | end; 295 | tmpList.Clear; //} 296 | 297 | // Scan Width and Height; 298 | SetLength(tmpStr, 1); 299 | i:=1; 300 | BlockRead(FileHDR, Pointer(@tmpStr[1])^, 1); 301 | while (tmpStr[i] <> Char(HeaderDelemiterRad)) do 302 | begin 303 | Inc(i); 304 | SetLength(tmpStr, i); 305 | BlockRead(FileHDR, Pointer(@tmpStr[i])^, 1); 306 | 307 | if (EOF(FileHDR)) then 308 | begin 309 | CloseFile(FileHDR); 310 | Result:=hdrBadScanWidth; 311 | Exit; 312 | end; 313 | end; 314 | 315 | tmpList.Delimiter:=' '; 316 | tmpList.DelimitedText:=tmpStr; 317 | if (tmpList.Count < 4) then 318 | begin 319 | CloseFile(FileHDR); 320 | tmpList.Destroy; 321 | Result:=hdrBadScanWidth; 322 | Exit; 323 | end; 324 | if ((tmpList.Strings[0] <> DirTopBottom) 325 | or (tmpList.Strings[2] <> DirLeftRight)) then 326 | begin 327 | CloseFile(FileHDR); 328 | tmpList.Destroy; 329 | Result:=hdrBadScanWidth; 330 | Exit; 331 | end; 332 | 333 | i:=StrToIntDef(tmpList.Strings[1], -1); 334 | if (i = -1) then 335 | begin 336 | CloseFile(FileHDR); 337 | tmpList.Destroy; 338 | Result:=hdrBadScanHeight; 339 | Exit; 340 | end; 341 | if (i <> Height) then 342 | begin 343 | CloseFile(FileHDR); 344 | tmpList.Destroy; 345 | Result:=hdrBadHeight; 346 | Exit; 347 | end; 348 | 349 | i:=StrToIntDef(tmpList.Strings[3], -1); 350 | if (i = -1) then 351 | begin 352 | CloseFile(FileHDR); 353 | tmpList.Destroy; 354 | Result:=hdrBadScanWidth; 355 | Exit; 356 | end; 357 | if (i <> Width) then 358 | begin 359 | CloseFile(FileHDR); 360 | tmpList.Destroy; 361 | Result:=hdrBadWidth; 362 | Exit; 363 | end; 364 | tmpList.Destroy; 365 | 366 | // Final - Read and unpack Pixel data 367 | SqrSize:=Width*Height; 368 | tmpPointer:=Lightmaps; 369 | if ((Width < 8) or (Width > $7fff)) then 370 | begin 371 | // run length encoding is not allowed so read flat*/ 372 | if ((Width > 0) and (Height > 0)) then 373 | begin 374 | SetLength(Buffer, SizeOf(tHDREntry)*SqrSize); 375 | BlockRead(FileHDR, Pointer(@Buffer[0])^, SizeOf(tHDREntry)*SqrSize); 376 | // Correct Lightmap RGBExp to Radiance RGBE 377 | for i:=0 to (SqrSize - 1) do 378 | begin 379 | tmpPointer.r:=Buffer[i*4]; 380 | tmpPointer.g:=Buffer[i*4 + 1]; 381 | tmpPointer.b:=Buffer[i*4 + 2]; 382 | tmpPointer.e:=Buffer[i*4 + 3] - 128; 383 | Inc(tmpPointer); 384 | end; 385 | end; 386 | end 387 | else 388 | begin 389 | try 390 | SetLength(Buffer, SizeOf(tHDREntry)*Width); 391 | for i:=0 to (Height - 1) do 392 | begin 393 | // Read RLD Header 394 | BlockRead(FileHDR, Pointer(@RunLenHeader[0])^, 4); 395 | 396 | // is this file is run length? 397 | if ((RunLenHeader[0] <> 2) 398 | or (RunLenHeader[1] <> 2) 399 | or (RunLenHeader[2] >= $80)) then 400 | begin 401 | // correct first pixel sample 402 | tmpPointer.r:=RunLenHeader[0]; 403 | tmpPointer.g:=RunLenHeader[1]; 404 | tmpPointer.b:=RunLenHeader[2]; 405 | tmpPointer.e:=RunLenHeader[3] - 128; 406 | Inc(tmpPointer); 407 | 408 | SetLength(Buffer, SizeOf(tHDREntry)*(SqrSize - 1)); 409 | BlockRead(FileHDR, Pointer(@Buffer[0])^, SizeOf(tHDREntry)*(SqrSize - 1)); 410 | // Correct Lightmap RGBExp to Radiance RGBE 411 | for j:=0 to (SqrSize - 1) do 412 | begin 413 | tmpPointer.r:=Buffer[j*4]; 414 | tmpPointer.g:=Buffer[j*4 + 1]; 415 | tmpPointer.b:=Buffer[j*4 + 2]; 416 | tmpPointer.e:=Buffer[j*4 + 3] - 128; 417 | Inc(tmpPointer); 418 | end; 419 | 420 | CloseFile(FileHDR); 421 | SetLength(Buffer, 0); 422 | Result:=hdrOK; 423 | Exit; 424 | end; 425 | 426 | // is correct width in RLE Header 427 | j:=(RunLenHeader[2] shl 8) + RunLenHeader[3]; 428 | if (j <> Width) then 429 | begin 430 | CloseFile(FileHDR); 431 | SetLength(Buffer, 0); 432 | Result:=hdrBadRLE; 433 | Exit; 434 | end; 435 | 436 | Ptr:=@Buffer[0]; 437 | // read each of the four channels for the scanline into the buffer 438 | for j:=0 to 3 do 439 | begin 440 | ptr_end:=@Buffer[(j + 1)*Width]; 441 | while (DWORD(ptr) < DWORD(ptr_end)) do 442 | begin 443 | BlockRead(FileHDR, Pointer(@BufWord[0])^, 2); 444 | 445 | if (BufWord[0] > 128) then 446 | begin 447 | // a run of the same value 448 | Count:=BufWord[0] - 128; 449 | if ( (Count = 0) 450 | or (Count > Integer(DWORD(ptr_end) - DWORD(ptr))) ) then 451 | begin 452 | CloseFile(FileHDR); 453 | SetLength(Buffer, 0); 454 | Result:=hdrBadRLE; 455 | Exit; 456 | end; 457 | 458 | for k:=0 to (Count - 1) do 459 | begin 460 | ptr^:=BufWord[1]; 461 | Inc(ptr); 462 | end; 463 | end 464 | else 465 | begin 466 | // a non-run 467 | Count:=BufWord[0]; 468 | if ( (Count = 0) 469 | or (Count > Integer(DWORD(ptr_end) - DWORD(ptr))) ) then 470 | begin 471 | CloseFile(FileHDR); 472 | SetLength(Buffer, 0); 473 | Result:=hdrBadRLE; 474 | Exit; 475 | end; 476 | 477 | ptr^:=BufWord[1]; 478 | Inc(ptr); 479 | Dec(Count); 480 | 481 | if (Count > 0) then 482 | begin 483 | BlockRead(FileHDR, Pointer(ptr)^, Count); 484 | Inc(ptr, Count); 485 | end; 486 | end; 487 | end; 488 | end; 489 | 490 | // now convert data from buffer into floats 491 | for j:=0 to (Width - 1) do 492 | begin 493 | tmpPointer.r:=Buffer[j]; 494 | tmpPointer.g:=Buffer[j + Width]; 495 | tmpPointer.b:=Buffer[j + 2*Width]; 496 | tmpPointer.e:=Buffer[j + 3*Width] - 128; 497 | Inc(tmpPointer); 498 | end; 499 | end; 500 | except 501 | CloseFile(FileHDR); 502 | SetLength(Buffer, 0); 503 | Result:=hdrBadRLE; 504 | Exit; 505 | end; 506 | end; 507 | 508 | CloseFile(FileHDR); 509 | SetLength(Buffer, 0); 510 | Result:=hdrOK; 511 | {$R+} 512 | end; 513 | 514 | function ShowLoadHDRError(const hdrError: eLoadStateHDR): String; 515 | begin 516 | {$R-} 517 | Result:=''; 518 | case (hdrError) of 519 | hdrOK: Result:='No Errors!'; 520 | hdrNoExists: Result:='File Not Exists!'; 521 | hdrMinSize: Result:='File have size less then ' + IntToStr(MinHDRFileSize) + ' Bytes!'; 522 | hdrBadHeader: Result:='File have bad header!'; 523 | hdrBadFormat: Result:='Unknown Data format, support only RGBE with RLE!'; 524 | hdrBadWidth: Result:='Width must be equal Lightmap Width!'; 525 | hdrBadHeight: Result:='Height must be equal Lightmap Height!'; 526 | hdrBadScanWidth: Result:='Width by File Header is NaN!'; 527 | hdrBadScanHeight: Result:='Height by File Header is NaN!'; 528 | hdrBadRLE: Result:='Error in RLE data unpack!'; 529 | end; 530 | {$R+} 531 | end; 532 | 533 | end. 534 | -------------------------------------------------------------------------------- /Modules/UnitRenderTimerManager.pas: -------------------------------------------------------------------------------- 1 | unit UnitRenderTimerManager; 2 | 3 | // Copyright (c) 2020 Sergey-KoRJiK, Belarus 4 | // github.com/Sergey-KoRJiK 5 | 6 | interface 7 | 8 | uses 9 | SysUtils, 10 | Windows; 11 | 12 | type CRenderTimerManager = class 13 | private 14 | fDeltaTime, fLastTime, fFPS: Double; 15 | fInitTimestamp: Double; // [seconds] 16 | iFrequency: Int64; 17 | iTmp: Int64; 18 | // 19 | function GetElaspedTime(): Double; 20 | public 21 | property ElaspedTime: Double read GetElaspedTime; // [sec] 22 | property DeltaTime: Double read fDeltaTime; // [sec] 23 | property FPS: Double read fFPS; // [1/sec] 24 | // 25 | constructor CreateManager(); 26 | destructor DeleteManager(); 27 | // 28 | procedure ResetTimer(); 29 | procedure ResetCounter(); 30 | // 31 | procedure UpdDeltaTime(); 32 | function GetStringFPS(): String; 33 | end; 34 | 35 | 36 | implementation 37 | 38 | 39 | constructor CRenderTimerManager.CreateManager(); 40 | begin 41 | {$R-} 42 | QueryPerformanceFrequency(Self.iFrequency); 43 | QueryPerformanceCounter(Self.iTmp); 44 | Self.fInitTimestamp:=Self.iTmp/Self.iFrequency; 45 | Self.fLastTime:=Self.fInitTimestamp; 46 | Self.fDeltaTime:=0.0; 47 | Self.fFPS:=0.0; 48 | {$R+} 49 | end; 50 | 51 | destructor CRenderTimerManager.DeleteManager(); 52 | begin 53 | {$R-} 54 | 55 | {$R+} 56 | end; 57 | 58 | 59 | function CRenderTimerManager.GetElaspedTime(): Double; 60 | begin 61 | {$R-} 62 | QueryPerformanceCounter(Self.iTmp); 63 | Result:=Self.iTmp/Self.iFrequency - Self.fInitTimestamp; 64 | {$R+} 65 | end; 66 | 67 | 68 | procedure CRenderTimerManager.ResetTimer(); 69 | begin 70 | {$R-} 71 | QueryPerformanceCounter(Self.iTmp); 72 | Self.fLastTime:=Self.iTmp/Self.iFrequency; 73 | Self.fDeltaTime:=0.0; 74 | Self.fFPS:=0.0; 75 | {$R+} 76 | end; 77 | 78 | procedure CRenderTimerManager.ResetCounter(); 79 | begin 80 | {$R-} 81 | QueryPerformanceCounter(Self.iTmp); 82 | fInitTimestamp:=Self.iTmp/Self.iFrequency; 83 | {$R+} 84 | end; 85 | 86 | procedure CRenderTimerManager.UpdDeltaTime(); 87 | var 88 | fCurrentTime: Double; 89 | begin 90 | {$R-} 91 | QueryPerformanceCounter(Self.iTmp); 92 | fCurrentTime:=Self.iTmp/Self.iFrequency; 93 | // 94 | Self.fDeltaTime:=fCurrentTime - Self.fLastTime; 95 | Self.fLastTime:=fCurrentTime; 96 | Self.fFPS:=1.0/Self.fDeltaTime; 97 | {$R+} 98 | end; 99 | 100 | function CRenderTimerManager.GetStringFPS(): String; 101 | begin 102 | {$R-} 103 | Result:=FloatToStrF(Self.fFPS, ffFixed, 6, 1); 104 | {$R+} 105 | end; 106 | 107 | end. 108 | -------------------------------------------------------------------------------- /Modules/UnitRenderingContextManager.pas: -------------------------------------------------------------------------------- 1 | unit UnitRenderingContextManager; 2 | 3 | // Copyright (c) 2020 Sergey-KoRJiK, Belarus 4 | // github.com/Sergey-KoRJiK 5 | 6 | interface 7 | 8 | uses 9 | SysUtils, 10 | Windows, 11 | Classes; 12 | 13 | type CRenderingContextManager = class 14 | private 15 | hWindowHandle: HWND; 16 | hDeviceContext: HDC; 17 | hRenderingContext: HGLRC; 18 | iPixelFormat: Integer; 19 | pfd: TPixelFormatDescriptor; 20 | // 21 | bValid: Boolean; 22 | public 23 | property IsValidRenderingContext: Boolean read bValid; 24 | // 25 | property PixelFormat: Integer read iPixelFormat; 26 | property WindowHandle: HWND read hWindowHandle; 27 | // 28 | constructor CreateManager(); 29 | destructor DeleteManager(); 30 | // 31 | function CreateRenderingContext(const WindowHandle: HWND; 32 | const DepthBits: Byte): Boolean; 33 | procedure MakeCurrent(); 34 | procedure UnMakeCurrent(); 35 | procedure SwapBuffers(); 36 | procedure DeleteRenderingContext(); 37 | // 38 | procedure ReadPixelFormatDescriptor(const ppfd: PPixelFormatDescriptor); 39 | end; 40 | 41 | 42 | implementation 43 | 44 | 45 | constructor CRenderingContextManager.CreateManager(); 46 | begin 47 | {$R-} 48 | Self.hWindowHandle:=0; 49 | Self.hDeviceContext:=0; 50 | Self.hRenderingContext:=0; 51 | Self.iPixelFormat:=-1; 52 | FillChar(Self.pfd, SizeOf(Self.pfd), 0); 53 | // 54 | Self.bValid:=False; 55 | {$R+} 56 | end; 57 | 58 | destructor CRenderingContextManager.DeleteManager(); 59 | begin 60 | {$R-} 61 | Self.DeleteRenderingContext(); 62 | {$R+} 63 | end; 64 | 65 | 66 | function CRenderingContextManager.CreateRenderingContext( 67 | const WindowHandle: HWND; const DepthBits: Byte): Boolean; 68 | begin 69 | {$R-} 70 | if (Self.bValid) then Self.DeleteRenderingContext(); 71 | 72 | if (IsWindow(WindowHandle) = False) then 73 | begin 74 | Result:=False; 75 | Exit; 76 | end; 77 | Self.hWindowHandle:=WindowHandle; 78 | 79 | FillChar(Self.pfd, SizeOf(Self.pfd), 0); 80 | Self.pfd.nSize:=SizeOf(pfd); 81 | Self.pfd.nVersion:=1; 82 | Self.pfd.dwFlags:=PFD_DRAW_TO_WINDOW or PFD_SUPPORT_OPENGL or PFD_DOUBLEBUFFER; 83 | Self.pfd.iPixelType:=PFD_TYPE_RGBA; 84 | Self.pfd.cDepthBits:=DepthBits; 85 | 86 | Self.hDeviceContext:=Windows.GetDC(Self.hWindowHandle); 87 | Self.iPixelFormat:=Windows.ChoosePixelFormat(Self.hDeviceContext, @Self.pfd); 88 | Windows.SetPixelFormat(Self.hDeviceContext, Self.iPixelFormat, @Self.pfd); 89 | Self.hRenderingContext:=Windows.wglCreateContext(Self.hDeviceContext); 90 | Self.bValid:=Boolean(Self.hRenderingContext <> 0); 91 | 92 | Result:=Self.bValid; 93 | {$R+} 94 | end; 95 | 96 | procedure CRenderingContextManager.MakeCurrent(); 97 | begin 98 | {$R-} 99 | if (Self.bValid) then 100 | begin 101 | Windows.wglMakeCurrent(Self.hDeviceContext, Self.hRenderingContext); 102 | end; 103 | {$R+} 104 | end; 105 | 106 | procedure CRenderingContextManager.UnMakeCurrent(); 107 | begin 108 | {$R-} 109 | Windows.wglMakeCurrent(0, 0); 110 | {$R+} 111 | end; 112 | 113 | procedure CRenderingContextManager.SwapBuffers(); 114 | begin 115 | {$R-} 116 | if (Self.bValid) then 117 | begin 118 | Windows.SwapBuffers(Self.hDeviceContext); 119 | end; 120 | {$R+} 121 | end; 122 | 123 | procedure CRenderingContextManager.DeleteRenderingContext(); 124 | begin 125 | {$R-} 126 | if (Self.bValid = False) then Exit; 127 | 128 | Self.UnMakeCurrent(); 129 | Windows.ReleaseDC(Self.hWindowHandle, Self.hDeviceContext); 130 | Windows.wglDeleteContext(Self.hDeviceContext); 131 | 132 | Self.hWindowHandle:=0; 133 | Self.hDeviceContext:=0; 134 | Self.hRenderingContext:=0; 135 | Self.iPixelFormat:=-1; 136 | FillChar(Self.pfd, SizeOf(Self.pfd), 0); 137 | 138 | Self.bValid:=False; 139 | {$R+} 140 | end; 141 | 142 | procedure CRenderingContextManager.ReadPixelFormatDescriptor( 143 | const ppfd: PPixelFormatDescriptor); 144 | begin 145 | {$R-} 146 | if ((Self.bValid = False) or (ppfd = nil)) then Exit; 147 | 148 | ppfd^:=Self.pfd; 149 | {$R+} 150 | end; 151 | 152 | end. 153 | -------------------------------------------------------------------------------- /Modules/UnitRescaling2D.pas: -------------------------------------------------------------------------------- 1 | unit UnitRescaling2D; 2 | 3 | // Copyright (c) 2020 Sergey-KoRJiK, Belarus 4 | // github.com/Sergey-KoRJiK 5 | 6 | interface 7 | 8 | uses 9 | Windows, 10 | UnitUserTypes; 11 | 12 | type tRescalingInfo = packed record 13 | SrcWidth, SrcHeight: Integer; 14 | DstWidth, DstHeight: Integer; 15 | SrcData, DstData: PByte; // Must be allocated 16 | end; 17 | type PRescalingInfo = ^tRescalingInfo; 18 | type ARescalingInfo = array of tRescalingInfo; 19 | 20 | 21 | function NearestRescaleValidateInfo(const lpInfo: PRescalingInfo): Boolean; 22 | 23 | function NearestRescaleRGBA8888(const lpInfo: PRescalingInfo): Boolean; 24 | 25 | 26 | implementation 27 | 28 | 29 | function NearestRescaleValidateInfo(const lpInfo: PRescalingInfo): Boolean; 30 | begin 31 | {$R-} 32 | Result:=Boolean( 33 | (lpInfo.SrcWidth > 0) and (lpInfo.SrcHeight > 0) and 34 | (lpInfo.DstWidth > 0) and (lpInfo.DstHeight > 0) and 35 | (lpInfo.SrcData <> nil) and (lpInfo.DstData <> nil) 36 | ); 37 | {$R+} 38 | end; 39 | 40 | function NearestRescaleRGBA8888(const lpInfo: PRescalingInfo): Boolean; 41 | var 42 | SrcX, SrcY, DstX, DstY: Integer; 43 | SrcOffset, DstOffset: Integer; 44 | ScaleX, ScaleY: Single; 45 | begin 46 | {$R-} 47 | if (NearestRescaleValidateInfo(lpInfo) = False) then 48 | begin 49 | Result:=False; 50 | Exit; 51 | end; 52 | 53 | ScaleX:=lpInfo.SrcWidth/lpInfo.DstWidth; 54 | ScaleY:=lpInfo.SrcHeight/lpInfo.DstHeight; 55 | // 56 | for DstY:=0 to (lpInfo.DstHeight - 1) do 57 | begin 58 | SrcY:=Round(DstY*ScaleY); 59 | if (SrcY >= lpInfo.SrcHeight) then SrcY:=lpInfo.SrcHeight - 1; 60 | // 61 | SrcOffset:=SrcY*lpInfo.SrcWidth; 62 | DstOffset:=DstY*lpInfo.DstWidth; 63 | for DstX:=0 to (lpInfo.DstWidth - 1) do 64 | begin 65 | SrcX:=Round(DstX*ScaleX); 66 | if (SrcX >= lpInfo.SrcWidth) then SrcX:=lpInfo.SrcWidth - 1; 67 | // 68 | ADWORD(lpInfo.DstData)[DstOffset + DstX]:=ADWORD(lpInfo.SrcData)[SrcOffset + SrcX]; 69 | end; 70 | end; 71 | 72 | Result:=True; 73 | {$R+} 74 | end; 75 | 76 | 77 | end. 78 | -------------------------------------------------------------------------------- /Modules/UnitShaderManager.pas: -------------------------------------------------------------------------------- 1 | unit UnitShaderManager; 2 | 3 | // Copyright (c) 2020 Sergey-KoRJiK, Belarus 4 | // github.com/Sergey-KoRJiK 5 | 6 | interface 7 | 8 | uses 9 | SysUtils, 10 | Windows, 11 | Classes, 12 | OpenGL, 13 | UnitOpenGLext; 14 | 15 | type CShaderManager = class 16 | private 17 | iVertexObj, iGeometryObj, iFragmentObj, iProgramObj: GLuint; 18 | bVertexFileLoad, bGeometryFileLoad, bFragmentFileLoad: Boolean; 19 | bVertexObjCreated, bGeometryObjCreated, bFragmentObjCreated: Boolean; 20 | bProgramObjLinked: Boolean; 21 | // 22 | public 23 | property isVertexShaderLoaded: Boolean read bVertexFileLoad; 24 | property isGeometryShaderLoaded: Boolean read bGeometryFileLoad; 25 | property isFragmentShaderLoaded: Boolean read bFragmentFileLoad; 26 | // 27 | property isVertexShaderCompiled: Boolean read bVertexObjCreated; 28 | property isGeometryShaderCompiled: Boolean read bGeometryObjCreated; 29 | property isFragmentShaderCompiled: Boolean read bFragmentObjCreated; 30 | property isProgramLinked: Boolean read bProgramObjLinked; 31 | // 32 | constructor CreateManager(); 33 | destructor DeleteManager(); 34 | // 35 | procedure Clear(); 36 | function CreateShadersAndProgram(const VertexShaderFN, GeometryShaderFN, 37 | FragmentShaderFN: String; const bEnableVertexShader, bEnableGeometryShader, 38 | bEnableFragmentShader: Boolean): String; // Return Log file 39 | // 40 | function UseProgram(): Boolean; 41 | procedure FihishUseProgram(); // call glUseProgram(0); 42 | // 43 | // Uniform** return True if UniformName exist and applied to program, 44 | // or return False if UniformName don't exists 45 | function Uniform1f(const UniformName: String; const v0: GLfloat): Boolean; 46 | function Uniform2f(const UniformName: String; const v0, v1: GLfloat): Boolean; 47 | function Uniform3f(const UniformName: String; const v0, v1, v2: GLfloat): Boolean; 48 | function Uniform4f(const UniformName: String; const v0, v1, v2, v3: GLfloat): Boolean; 49 | // 50 | function Uniform1i(const UniformName: String; const v0: GLint): Boolean; 51 | function Uniform2i(const UniformName: String; const v0, v1: GLint): Boolean; 52 | function Uniform3i(const UniformName: String; const v0, v1, v2: GLint): Boolean; 53 | function Uniform4i(const UniformName: String; const v0, v1, v2, v3: GLint): Boolean; 54 | // 55 | // VectorDataType = {GL_INT, GL_UNSIGNED_INT, GL_FLOAT}. 56 | // NumOfComponents = number of VectorPtr components. 57 | // ArrayLength = length of array, if VectorPtr is link to array of Vectors. 58 | // ArrayLength = 1, if VectorPtr is link to single Vector; 59 | function UniformPtr(const UniformName: String; const VectorDataType: GLenum; 60 | const NumOfComponents, ArrayLength: Integer; const VectorPtr: PGLvoid): Boolean; 61 | // 62 | // MatrixSize = 2, 3 or 4 - size of square matrix. 63 | // transpose - in need transpose matrix before send to program. 64 | // ArrayLength = length of array, if MatrixPtr is link to array of Matrix. 65 | // ArrayLength = 1, if MatrixPtr is link to single Matrix; 66 | function UniformMatrix(const UniformName: String; const transpose: GLboolean; 67 | const MatrixSize, ArrayLength: Integer; const MatrixPtr: PGLfloat): Boolean; 68 | // 69 | // Function UniformTexture equivalent next 3 code lines: 70 | // 1. glActiveTextureARB(GL_TEXTURE0 + ActiveTexNumber); 71 | // 2. glBindTexture(TexTarget, TexId); 72 | // 3. glUniform1iARB(glGetUniformLocationARB(programObj, TextureName), ActiveTexNumber); 73 | // TexTarget is one of GL_TEXTURE_1D and ect. 74 | // ActiveTexNumber = [0..(MaxCount - 1)], where GLint MaxCount can be founded as 75 | // glGetIntegerv(GL_MAX_TEXTURE_UNITS, @MaxCount); 76 | function UniformTexture(const TextureName: String; const TexTarget: GLenum; 77 | const ActiveTexNumber, TexId: GLuint): Boolean; 78 | end; 79 | 80 | 81 | implementation 82 | 83 | 84 | constructor CShaderManager.CreateManager(); 85 | begin 86 | {$R-} 87 | Self.iVertexObj:=0; 88 | Self.iGeometryObj:=0; 89 | Self.iFragmentObj:=0; 90 | Self.iProgramObj:=0; 91 | // 92 | Self.bVertexObjCreated:=False; 93 | Self.bGeometryObjCreated:=False; 94 | Self.bFragmentObjCreated:=False; 95 | Self.bProgramObjLinked:=False; 96 | // 97 | Self.bVertexFileLoad:=False; 98 | Self.bGeometryFileLoad:=False; 99 | Self.bFragmentFileLoad:=False; 100 | {$R+} 101 | end; 102 | 103 | destructor CShaderManager.DeleteManager(); 104 | begin 105 | {$R-} 106 | Self.Clear(); 107 | {$R+} 108 | end; 109 | 110 | 111 | procedure CShaderManager.Clear(); 112 | begin 113 | {$R-} 114 | glDetachObjectARB(Self.iProgramObj, Self.iVertexObj); 115 | glDetachObjectARB(Self.iProgramObj, Self.iGeometryObj); 116 | glDetachObjectARB(Self.iProgramObj, Self.iFragmentObj); 117 | // 118 | glDeleteObjectARB(Self.iVertexObj); 119 | glDeleteObjectARB(Self.iGeometryObj); 120 | glDeleteObjectARB(Self.iFragmentObj); 121 | // 122 | glDeleteObjectARB(Self.iProgramObj); 123 | 124 | Self.bVertexFileLoad:=False; 125 | Self.bGeometryFileLoad:=False; 126 | Self.bFragmentFileLoad:=False; 127 | // 128 | Self.bVertexObjCreated:=False; 129 | Self.bGeometryObjCreated:=False; 130 | Self.bFragmentObjCreated:=False; 131 | Self.bProgramObjLinked:=False; 132 | {$R+} 133 | end; 134 | 135 | function CShaderManager.CreateShadersAndProgram( 136 | const VertexShaderFN, GeometryShaderFN, FragmentShaderFN: String; 137 | const bEnableVertexShader, bEnableGeometryShader, bEnableFragmentShader: Boolean): String; 138 | var 139 | ShaderFile: File; 140 | ShaderCodeStr, LogStr: AGLChar; 141 | ShaderCodeLen: Integer; 142 | CompileState, MaxLogLen: GLint; 143 | // 144 | VertexInfoStr, GeometryInfoStr, FragmentInfoStr: String; 145 | ProgramInfoStr: String; 146 | begin 147 | {$R-} 148 | Self.Clear(); 149 | if (bEnableVertexShader or bEnableGeometryShader or bEnableFragmentShader) then 150 | begin 151 | if (bEnableVertexShader) then 152 | begin 153 | // Load and Create Vertex Shader 154 | if (FileExists(VertexShaderFN)) then 155 | begin 156 | // 1. Load code from text file 157 | AssignFile(ShaderFile, VertexShaderFN); 158 | Reset(ShaderFile, 1); 159 | // 160 | // Get shader code length and allocate memory for code 161 | ShaderCodeLen:=FileSize(ShaderFile); 162 | SetLength(ShaderCodeStr, ShaderCodeLen + 1); 163 | // 164 | // Fill memory of code with zero char - make null-terminated string 165 | FillChar(ShaderCodeStr[0], ShaderCodeLen + 1, #0); 166 | // 167 | // Load code from file to memory and close file. 168 | // Set vertex code file load flag = True 169 | BlockRead(ShaderFile, ShaderCodeStr[0], ShaderCodeLen); 170 | CloseFile(ShaderFile); 171 | Self.bVertexFileLoad:=True; 172 | 173 | // 2. Create and compile shader from loaded code 174 | Self.iVertexObj:=glCreateShaderObjectARB(GL_VERTEX_SHADER); 175 | glShaderSourceARB(Self.iVertexObj, 1, PPGLchar(@PGLchar(ShaderCodeStr)), nil); 176 | glCompileShaderARB(Self.iVertexObj); 177 | // 178 | // free temporary loaded code data memory 179 | ShaderCodeLen:=0; 180 | SetLength(ShaderCodeStr, ShaderCodeLen); 181 | // 182 | // Get compilations status and set shader create flag by status value 183 | glGetObjectParameterivARB(Self.iVertexObj, GL_OBJECT_COMPILE_STATUS, @CompileState); 184 | Self.bVertexObjCreated:=GLboolean(CompileState); 185 | // 186 | // Get length of generated compile log, allocate memory for log data 187 | // and extract compilation log 188 | glGetObjectParameterivARB(Self.iVertexObj, GL_OBJECT_INFO_LOG_LENGTH, @MaxLogLen); 189 | SetLength(LogStr, MaxLogLen + 1); 190 | glGetInfoLogARB(Self.iVertexObj, MaxLogLen, @MaxLogLen, @LogStr[0]); 191 | // 192 | // Generate text info 193 | if (Self.bVertexObjCreated) then 194 | begin 195 | VertexInfoStr:='OK: Vertex shader created: ' + #10 + PChar(LogStr); 196 | end 197 | else 198 | begin 199 | VertexInfoStr:='ERROR: Vertex shader not created: ' + #10 + PChar(LogStr); 200 | glDeleteObjectARB(Self.iVertexObj); 201 | end; 202 | // 203 | // Free temporary log memory 204 | SetLength(LogStr, 0); 205 | end 206 | else 207 | begin 208 | // Generate text info, set vertex code file load flag = False 209 | VertexInfoStr:='ERROR: invalid file path' + #10 + VertexShaderFN; 210 | Self.bVertexFileLoad:=False; 211 | end; 212 | end 213 | else 214 | begin 215 | // Generate text info 216 | VertexInfoStr:='Vertex shader is disabled for create.'; 217 | end; 218 | 219 | if (bEnableGeometryShader) then 220 | begin 221 | // Load and Create Geometry Shader 222 | if (FileExists(GeometryShaderFN)) then 223 | begin 224 | // 1. Load code from text file 225 | AssignFile(ShaderFile, GeometryShaderFN); 226 | Reset(ShaderFile, 1); 227 | // 228 | // Get shader code length and allocate memory for code 229 | ShaderCodeLen:=FileSize(ShaderFile); 230 | SetLength(ShaderCodeStr, ShaderCodeLen + 1); 231 | // 232 | // Fill memory of code with zero char - make null-terminated string 233 | FillChar(ShaderCodeStr[0], ShaderCodeLen + 1, #0); 234 | // 235 | // Load code from file to memory and close file. 236 | // Set geometry code file load flag = True 237 | BlockRead(ShaderFile, ShaderCodeStr[0], ShaderCodeLen); 238 | CloseFile(ShaderFile); 239 | Self.bGeometryFileLoad:=True; 240 | 241 | // 2. Create and compile shader from loaded code 242 | Self.iGeometryObj:=glCreateShaderObjectARB(GL_GEOMETRY_SHADER); 243 | glShaderSourceARB(Self.iGeometryObj, 1, PPGLchar(@PGLchar(ShaderCodeStr)), nil); 244 | glCompileShaderARB(Self.iGeometryObj); 245 | // 246 | // free temporary loaded code data memory 247 | ShaderCodeLen:=0; 248 | SetLength(ShaderCodeStr, ShaderCodeLen); 249 | // 250 | // Get compilations status and set shader create flag by status value 251 | glGetObjectParameterivARB(Self.iGeometryObj, GL_OBJECT_COMPILE_STATUS, @CompileState); 252 | Self.bGeometryObjCreated:=GLboolean(CompileState); 253 | // 254 | // Get length of generated compile log, allocate memory for log data 255 | // and extract compilation log 256 | glGetObjectParameterivARB(Self.iGeometryObj, GL_OBJECT_INFO_LOG_LENGTH, @MaxLogLen); 257 | SetLength(LogStr, MaxLogLen + 1); 258 | glGetInfoLogARB(Self.iGeometryObj, MaxLogLen, @MaxLogLen, @LogStr[0]); 259 | // 260 | // Generate text info 261 | if (Self.bGeometryObjCreated) then 262 | begin 263 | GeometryInfoStr:='OK: Geometry shader created: ' + #10 + PChar(LogStr); 264 | end 265 | else 266 | begin 267 | GeometryInfoStr:='ERROR: Geometry shader not created: ' + #10 + PChar(LogStr); 268 | glDeleteObjectARB(Self.iGeometryObj); 269 | end; 270 | // 271 | // Free temporary log memory 272 | SetLength(LogStr, 0); 273 | end 274 | else 275 | begin 276 | // Generate text info, set geometry code file load flag = False 277 | GeometryInfoStr:='ERROR: invalid file path' + #10 + GeometryShaderFN; 278 | Self.bGeometryFileLoad:=False; 279 | end; 280 | end 281 | else 282 | begin 283 | // Generate text info 284 | GeometryInfoStr:='Geometry shader is disabled for create.'; 285 | end; 286 | 287 | if (bEnableFragmentShader) then 288 | begin 289 | // Load and Create Fragment Shader 290 | if (FileExists(FragmentShaderFN)) then 291 | begin 292 | // 1. Load code from text file 293 | AssignFile(ShaderFile, FragmentShaderFN); 294 | Reset(ShaderFile, 1); 295 | // 296 | // Get shader code length and allocate memory for code 297 | ShaderCodeLen:=FileSize(ShaderFile); 298 | SetLength(ShaderCodeStr, ShaderCodeLen + 1); 299 | // 300 | // Fill memory of code with zero char - make null-terminated string 301 | FillChar(ShaderCodeStr[0], ShaderCodeLen + 1, #0); 302 | // 303 | // Load code from file to memory and close file. 304 | // Set fragment code file load flag = True 305 | BlockRead(ShaderFile, ShaderCodeStr[0], ShaderCodeLen); 306 | CloseFile(ShaderFile); 307 | Self.bGeometryFileLoad:=True; 308 | 309 | // 2. Create and compile shader from loaded code 310 | Self.iFragmentObj:=glCreateShaderObjectARB(GL_FRAGMENT_SHADER); 311 | glShaderSourceARB(Self.iFragmentObj, 1, PPGLchar(@PGLchar(ShaderCodeStr)), nil); 312 | glCompileShaderARB(Self.iFragmentObj); 313 | // 314 | // free temporary loaded code data memory 315 | ShaderCodeLen:=0; 316 | SetLength(ShaderCodeStr, ShaderCodeLen); 317 | // 318 | // Get compilations status and set shader create flag by status value 319 | glGetObjectParameterivARB(Self.iFragmentObj, GL_OBJECT_COMPILE_STATUS, @CompileState); 320 | Self.bFragmentObjCreated:=GLboolean(CompileState); 321 | // 322 | // Get length of generated compile log, allocate memory for log data 323 | // and extract compilation log 324 | glGetObjectParameterivARB(Self.iFragmentObj, GL_OBJECT_INFO_LOG_LENGTH, @MaxLogLen); 325 | SetLength(LogStr, MaxLogLen + 1); 326 | glGetInfoLogARB(Self.iFragmentObj, MaxLogLen, @MaxLogLen, @LogStr[0]); 327 | // 328 | // Generate text info 329 | if (Self.bFragmentObjCreated) then 330 | begin 331 | FragmentInfoStr:='OK: Fragment shader created: ' + #10 + PChar(LogStr); 332 | end 333 | else 334 | begin 335 | FragmentInfoStr:='ERROR: Fragment shader not created: ' + #10 + PChar(LogStr); 336 | glDeleteObjectARB(Self.iFragmentObj); 337 | end; 338 | // 339 | // Free temporary log memory 340 | SetLength(LogStr, 0); 341 | end 342 | else 343 | begin 344 | // Generate text info, set fragment code file load flag = False 345 | FragmentInfoStr:='ERROR: invalid file path' + #10 + FragmentShaderFN; 346 | Self.bFragmentFileLoad:=False; 347 | end; 348 | end 349 | else 350 | begin 351 | // Generate text info 352 | FragmentInfoStr:='Fragment shader is disabled for create.'; 353 | end; 354 | 355 | // Next - create programm process 356 | if ((Self.bVertexObjCreated or Self.bGeometryObjCreated 357 | or Self.bFragmentObjCreated) = False) then 358 | begin 359 | // If no one shader created - do not create program and exit 360 | // Generate text info 361 | Self.bProgramObjLinked:=False; 362 | ProgramInfoStr:='ERROR: no one shader created - program creating disable!'; 363 | end 364 | else 365 | begin 366 | // Create program object 367 | Self.iProgramObj:=glCreateProgramObjectARB(); 368 | // 369 | // attach created shaders, if it's aviable 370 | if (Self.bVertexObjCreated) then glAttachObjectARB(Self.iProgramObj, Self.iVertexObj); 371 | if (Self.bGeometryObjCreated) then glAttachObjectARB(Self.iProgramObj, Self.iGeometryObj); 372 | if (Self.bFragmentObjCreated) then glAttachObjectARB(Self.iProgramObj, Self.iFragmentObj); 373 | // 374 | // link attached shaders to program 375 | glLinkProgramARB(Self.iProgramObj); 376 | // 377 | // Get link status and set shader create flag by status value 378 | glGetObjectParameterivARB(Self.iProgramObj, GL_OBJECT_LINK_STATUS, @CompileState); 379 | Self.bProgramObjLinked:=GLboolean(CompileState); 380 | // 381 | // Get length of generated link log, allocate memory for log data 382 | // and extract compilation log 383 | glGetObjectParameterivARB(Self.iProgramObj, GL_OBJECT_INFO_LOG_LENGTH, @MaxLogLen); 384 | SetLength(LogStr, MaxLogLen + 1); 385 | glGetInfoLogARB(Self.iProgramObj, MaxLogLen, @MaxLogLen, @LogStr[0]); 386 | // 387 | // Generate text info 388 | if (Self.bProgramObjLinked) then 389 | begin 390 | ProgramInfoStr:='OK: Program linked: ' + #10 + PChar(LogStr); 391 | end 392 | else 393 | begin 394 | ProgramInfoStr:='ERROR: Program link failed: ' + #10 + PChar(LogStr); 395 | glDeleteObjectARB(Self.iProgramObj); 396 | glDeleteObjectARB(Self.iVertexObj); 397 | glDeleteObjectARB(Self.iGeometryObj); 398 | glDeleteObjectARB(Self.iFragmentObj); 399 | end; 400 | // 401 | // Free temporary log memory, detach shaders from program 402 | SetLength(LogStr, 0); 403 | glDetachObjectARB(Self.iProgramObj, Self.iVertexObj); 404 | glDetachObjectARB(Self.iProgramObj, Self.iGeometryObj); 405 | glDetachObjectARB(Self.iProgramObj, Self.iFragmentObj); 406 | end; 407 | 408 | // Make final log 409 | Result:= 410 | VertexInfoStr + #10 + 411 | GeometryInfoStr + #10 + 412 | FragmentInfoStr + #10 + 413 | ProgramInfoStr; 414 | end 415 | else 416 | begin 417 | Result:='Shader not created: no one shader selected for create!'; 418 | end; 419 | {$R+} 420 | end; 421 | 422 | 423 | 424 | 425 | function CShaderManager.UseProgram(): Boolean; 426 | begin 427 | {$R-} 428 | if (Self.bProgramObjLinked) then glUseProgramObjectARB(Self.iProgramObj); 429 | Result:=Self.bProgramObjLinked; 430 | {$R+} 431 | end; 432 | 433 | procedure CShaderManager.FihishUseProgram(); 434 | begin 435 | {$R-} 436 | if (Self.bProgramObjLinked) then glUseProgramObjectARB(0); 437 | {$R+} 438 | end; 439 | 440 | 441 | function CShaderManager.Uniform1f(const UniformName: String; 442 | const v0: GLfloat): Boolean; 443 | var 444 | tmpStr: String; 445 | id: GLint; 446 | begin 447 | {$R-} 448 | if (Self.bProgramObjLinked) then 449 | begin 450 | tmpStr:=UniformName + #0; // Make null-terminated string 451 | id:=glGetUniformLocationARB(Self.iProgramObj, PGLChar(tmpStr)); 452 | if (id < 0) then 453 | begin 454 | Result:=False; 455 | end 456 | else 457 | begin 458 | glUniform1fARB(id, v0); 459 | Result:=True; 460 | end; 461 | end 462 | else Result:=False; 463 | {$R+} 464 | end; 465 | 466 | function CShaderManager.Uniform2f(const UniformName: String; 467 | const v0, v1: GLfloat): Boolean; 468 | var 469 | tmpStr: String; 470 | id: GLint; 471 | begin 472 | {$R-} 473 | if (Self.bProgramObjLinked) then 474 | begin 475 | tmpStr:=UniformName + #0; // Make null-terminated string 476 | id:=glGetUniformLocationARB(Self.iProgramObj, PGLChar(tmpStr)); 477 | if (id < 0) then 478 | begin 479 | Result:=False; 480 | end 481 | else 482 | begin 483 | glUniform2fARB(id, v0, v1); 484 | Result:=True; 485 | end; 486 | end 487 | else Result:=False; 488 | {$R+} 489 | end; 490 | 491 | function CShaderManager.Uniform3f(const UniformName: String; 492 | const v0, v1, v2: GLfloat): Boolean; 493 | var 494 | tmpStr: String; 495 | id: GLint; 496 | begin 497 | {$R-} 498 | if (Self.bProgramObjLinked) then 499 | begin 500 | tmpStr:=UniformName + #0; // Make null-terminated string 501 | id:=glGetUniformLocationARB(Self.iProgramObj, PGLChar(tmpStr)); 502 | if (id < 0) then 503 | begin 504 | Result:=False; 505 | end 506 | else 507 | begin 508 | glUniform3fARB(id, v0, v1, v2); 509 | Result:=True; 510 | end; 511 | end 512 | else Result:=False; 513 | {$R+} 514 | end; 515 | 516 | function CShaderManager.Uniform4f(const UniformName: String; 517 | const v0, v1, v2, v3: GLfloat): Boolean; 518 | var 519 | tmpStr: String; 520 | id: GLint; 521 | begin 522 | {$R-} 523 | if (Self.bProgramObjLinked) then 524 | begin 525 | tmpStr:=UniformName + #0; // Make null-terminated string 526 | id:=glGetUniformLocationARB(Self.iProgramObj, PGLChar(tmpStr)); 527 | if (id < 0) then 528 | begin 529 | Result:=False; 530 | end 531 | else 532 | begin 533 | glUniform4fARB(id, v0, v1, v2, v3); 534 | Result:=True; 535 | end; 536 | end 537 | else Result:=False; 538 | {$R+} 539 | end; 540 | 541 | 542 | function CShaderManager.Uniform1i(const UniformName: String; 543 | const v0: GLint): Boolean; 544 | var 545 | tmpStr: String; 546 | id: GLint; 547 | begin 548 | {$R-} 549 | if (Self.bProgramObjLinked) then 550 | begin 551 | tmpStr:=UniformName + #0; // Make null-terminated string 552 | id:=glGetUniformLocationARB(Self.iProgramObj, PGLChar(tmpStr)); 553 | if (id < 0) then 554 | begin 555 | Result:=False; 556 | end 557 | else 558 | begin 559 | glUniform1iARB(id, v0); 560 | Result:=True; 561 | end; 562 | end 563 | else Result:=False; 564 | {$R+} 565 | end; 566 | 567 | function CShaderManager.Uniform2i(const UniformName: String; 568 | const v0, v1: GLint): Boolean; 569 | var 570 | tmpStr: String; 571 | id: GLint; 572 | begin 573 | {$R-} 574 | if (Self.bProgramObjLinked) then 575 | begin 576 | tmpStr:=UniformName + #0; // Make null-terminated string 577 | id:=glGetUniformLocationARB(Self.iProgramObj, PGLChar(tmpStr)); 578 | if (id < 0) then 579 | begin 580 | Result:=False; 581 | end 582 | else 583 | begin 584 | glUniform2iARB(id, v0, v1); 585 | Result:=True; 586 | end; 587 | end 588 | else Result:=False; 589 | {$R+} 590 | end; 591 | 592 | function CShaderManager.Uniform3i(const UniformName: String; 593 | const v0, v1, v2: GLint): Boolean; 594 | var 595 | tmpStr: String; 596 | id: GLint; 597 | begin 598 | {$R-} 599 | if (Self.bProgramObjLinked) then 600 | begin 601 | tmpStr:=UniformName + #0; // Make null-terminated string 602 | id:=glGetUniformLocationARB(Self.iProgramObj, PGLChar(tmpStr)); 603 | if (id < 0) then 604 | begin 605 | Result:=False; 606 | end 607 | else 608 | begin 609 | glUniform3iARB(id, v0, v1, v2); 610 | Result:=True; 611 | end; 612 | end 613 | else Result:=False; 614 | {$R+} 615 | end; 616 | 617 | function CShaderManager.Uniform4i(const UniformName: String; 618 | const v0, v1, v2, v3: GLint): Boolean; 619 | var 620 | tmpStr: String; 621 | id: GLint; 622 | begin 623 | {$R-} 624 | if (Self.bProgramObjLinked) then 625 | begin 626 | tmpStr:=UniformName + #0; // Make null-terminated string 627 | id:=glGetUniformLocationARB(Self.iProgramObj, PGLChar(tmpStr)); 628 | if (id < 0) then 629 | begin 630 | Result:=False; 631 | end 632 | else 633 | begin 634 | glUniform4iARB(id, v0, v1, v2, v3); 635 | Result:=True; 636 | end; 637 | end 638 | else Result:=False; 639 | {$R+} 640 | end; 641 | 642 | 643 | function CShaderManager.UniformPtr(const UniformName: String; 644 | const VectorDataType: GLenum; const NumOfComponents, ArrayLength: Integer; 645 | const VectorPtr: PGLvoid): Boolean; 646 | var 647 | tmpStr: String; 648 | id: GLint; 649 | begin 650 | {$R-} 651 | if (ArrayLength < 1) then 652 | begin 653 | Result:=False; 654 | Exit; 655 | end; 656 | 657 | if (Self.bProgramObjLinked) then 658 | begin 659 | tmpStr:=UniformName + #0; // Make null-terminated string 660 | id:=glGetUniformLocationARB(Self.iProgramObj, PGLChar(tmpStr)); 661 | if (id < 0) then 662 | begin 663 | Result:=False; 664 | end 665 | else 666 | begin 667 | Result:=True; 668 | case (VectorDataType) of 669 | GL_INT: 670 | begin 671 | case (NumOfComponents) of 672 | 1: glUniform1ivARB(id, ArrayLength, VectorPtr); 673 | 2: glUniform2ivARB(id, ArrayLength, VectorPtr); 674 | 3: glUniform3ivARB(id, ArrayLength, VectorPtr); 675 | 4: glUniform4ivARB(id, ArrayLength, VectorPtr); 676 | else 677 | Result:=False; 678 | end; 679 | end; 680 | GL_FLOAT: 681 | begin 682 | case (NumOfComponents) of 683 | 1: glUniform1fvARB(id, ArrayLength, VectorPtr); 684 | 2: glUniform2fvARB(id, ArrayLength, VectorPtr); 685 | 3: glUniform3fvARB(id, ArrayLength, VectorPtr); 686 | 4: glUniform4fvARB(id, ArrayLength, VectorPtr); 687 | else 688 | Result:=False; 689 | end; 690 | end; 691 | else 692 | Result:=False; 693 | end; 694 | end; 695 | end 696 | else Result:=False; 697 | {$R+} 698 | end; 699 | 700 | function CShaderManager.UniformMatrix(const UniformName: String; 701 | const transpose: GLboolean; const MatrixSize, ArrayLength: Integer; 702 | const MatrixPtr: PGLfloat): Boolean; 703 | var 704 | tmpStr: String; 705 | id: GLint; 706 | begin 707 | {$R-} 708 | if (ArrayLength < 1) then 709 | begin 710 | Result:=False; 711 | Exit; 712 | end; 713 | 714 | if (Self.bProgramObjLinked) then 715 | begin 716 | tmpStr:=UniformName + #0; // Make null-terminated string 717 | id:=glGetUniformLocationARB(Self.iProgramObj, PGLChar(tmpStr)); 718 | if (id < 0) then 719 | begin 720 | Result:=False; 721 | end 722 | else 723 | begin 724 | Result:=True; 725 | case (MatrixSize) of 726 | 2: glUniformMatrix2fvARB(id, ArrayLength, transpose, MatrixPtr); 727 | 3: glUniformMatrix3fvARB(id, ArrayLength, transpose, MatrixPtr); 728 | 4: glUniformMatrix4fvARB(id, ArrayLength, transpose, MatrixPtr); 729 | else 730 | Result:=False; 731 | end; 732 | end; 733 | end 734 | else Result:=False; 735 | {$R+} 736 | end; 737 | 738 | function CShaderManager.UniformTexture(const TextureName: String; 739 | const TexTarget: GLenum; const ActiveTexNumber, TexId: GLuint): Boolean; 740 | var 741 | tmpStr: String; 742 | id: GLint; 743 | begin 744 | {$R-} 745 | if (Self.bProgramObjLinked) then 746 | begin 747 | tmpStr:=TextureName + #0; // Make null-terminated string 748 | id:=glGetUniformLocationARB(Self.iProgramObj, PGLChar(tmpStr)); 749 | if (id < 0) then 750 | begin 751 | Result:=False; 752 | end 753 | else 754 | begin 755 | Result:=True; 756 | glActiveTextureARB(GL_TEXTURE0 + ActiveTexNumber); 757 | glBindTexture(TexTarget, TexId); 758 | glUniform1iARB(id, ActiveTexNumber); 759 | end; 760 | end 761 | else Result:=False; 762 | {$R+} 763 | end; 764 | 765 | end. 766 | -------------------------------------------------------------------------------- /Modules/UnitUserTypes.pas: -------------------------------------------------------------------------------- 1 | unit UnitUserTypes; 2 | 3 | // Copyright (c) 2020 Sergey-KoRJiK, Belarus 4 | // github.com/Sergey-KoRJiK 5 | 6 | interface 7 | 8 | uses 9 | Windows; 10 | 11 | type tColor4fv = array[0..3] of Single; 12 | 13 | type tVec2f = packed record 14 | x, y: Single; 15 | end; 16 | type PVec2f = ^tVec2f; 17 | type AVec2f = array of tVec2f; 18 | 19 | // BSP Vertex def too 20 | type tVec3f = packed record 21 | x, y, z: Single; 22 | end; 23 | type PVec3f = ^tVec3f; 24 | type AVec3f = array of tVec3f; 25 | 26 | type tVec4f = packed record 27 | case Byte of 28 | 0: (Vec3f: tVec3f; pad: DWORD;); 29 | 1: (x, y, z, w: Single;); 30 | 2: (v: array[0..3] of Single;); 31 | end; 32 | type PVec4f = ^tVec4f; 33 | type AVec4f = array of tVec4f; 34 | 35 | 36 | type tMat3f = array[0..8] of Single; 37 | type PMat3f = ^tMat3f; 38 | 39 | type tMat4f = array[0..15] of Single; 40 | type PMat4f = ^tMat4f; 41 | 42 | type tLightmap = packed record 43 | r, g, b: Byte; 44 | e: ShortInt; 45 | end; // 4 Bytes 46 | type PLightmap = ^tLightmap; 47 | type ALightmap = array of tLightmap; 48 | 49 | type tCompressedLightCube = array[0..5] of tLightmap; // 24 Bytes 50 | type PCompressedLightCube = ^tCompressedLightCube; 51 | type ACompressedLightCube = array of tCompressedLightCube; 52 | 53 | // 1 / (c + l * dist + q * dist^2) 54 | type tAttenuate3f = packed record 55 | c, l, q: Single; 56 | end; // 12 Bytes 57 | type PAttenuate3f = ^tAttenuate3f; 58 | type AAttenuate3f = array of tAttenuate3f; 59 | 60 | 61 | type tBBOX3f = packed record 62 | vMin, vMax: tVec3f; 63 | end; 64 | type PBBOX3f = ^tBBOX3f; 65 | type ABBOX3f = array of tBBOX3f; 66 | 67 | type tBBOX4f = packed record 68 | vMin, vMax: tVec4f; 69 | end; 70 | type PBBOX4f = ^tBBOX4f; 71 | type ABBOX4f = array of tBBOX4f; 72 | 73 | type tTexBBOXf = packed record 74 | vMin, vMax: tVec2f; 75 | end; 76 | type PTexBBOXf = ^tTexBBOXf; 77 | 78 | type tVec2b = packed record 79 | x, y: Byte; 80 | end; // 2 Bytes 81 | type PVec2b = ^tVec2b; 82 | type AVec2b = array of tVec2b; 83 | 84 | type tVec2s = packed record 85 | x, y: SmallInt; 86 | end; // 4 Bytes 87 | type PVec2s = ^tVec2s; 88 | type AVec2s = array of tVec2s; 89 | 90 | type tVec2i = packed record 91 | x, y: Integer; 92 | end; // 8 Bytes 93 | type PVec2i = ^tVec2i; 94 | type AVec2i = array of tVec2i; 95 | 96 | type tVec3b = packed record 97 | x, y, z: Byte; 98 | end; // 3 Bytes 99 | type PVec3b = ^tVec3b; 100 | type AVec3b = array of tVec3b; 101 | 102 | type tVec3s = packed record 103 | x, y, z: SmallInt; 104 | end; // 6 Bytes 105 | type PVec3s = ^tVec3s; 106 | type AVec3s = array of tVec3s; 107 | 108 | type tVec3i = packed record 109 | x, y, z: Integer; 110 | end; // 12 Bytes 111 | type PVec3i = ^tVec3i; 112 | type AVec3i = array of tVec3i; 113 | 114 | type tVec4b = packed record 115 | x, y, z, w: Byte; 116 | end; // 4 Bytes 117 | type PVec4b = ^tVec4b; 118 | type AVec4b = ^tVec4b; 119 | 120 | type tBBOXs = packed record 121 | nMin, nMax: tVec3s; 122 | end; 123 | type PBBOXs = ^tBBOXs; 124 | type ABBOXs = array of tBBOXs; 125 | 126 | type tRay = packed record 127 | Start, Dir: tVec4f; // Dir must be normalized 128 | end; // 32 Bytes 129 | type PRay = ^tRay; 130 | type ARay = array of tRay; 131 | 132 | // Plane equation dot(Normal, PointOnPlane) = Dist 133 | type tPlane = record 134 | Normal: tVec3f; 135 | Dist: Single; 136 | AxisType: Integer; 137 | end; // 20 Bytes 138 | type PPlane = ^tPlane; 139 | type APlane = array of tPlane; 140 | 141 | // Each row is 16-bytes full XMM-register 142 | type tPlanePacket = packed record 143 | Nx: array[0..3] of Single; 144 | Ny: array[0..3] of Single; 145 | Nz: array[0..3] of Single; 146 | fD: array[0..3] of Single; 147 | end; // 64 Bytes 148 | type PPlanePacket = ^tPlanePacket; 149 | type APlanePacket = array of tPlanePacket; 150 | 151 | type tPolygon3f = packed record 152 | // Plane section: 153 | Plane : tPlane; 154 | Center : tVec4f; 155 | // Vertex section: 156 | CountVertecies: Integer; 157 | CountTriangles: Integer;// = CountVertecies - 2 158 | Vertecies : AVec4f; // size = CountVertecies 159 | // Edge section: 160 | CountPackets : Integer; 161 | CountPlanes : Integer; // Count of non-colinears planes 162 | SidePlanes : APlanePacket; // size = Ceil(CountVertecies/4) 163 | 164 | padding : Integer; 165 | end; // 64 Bytes 166 | type PPolygon3f = ^tPolygon3f; 167 | type APolygon3f = array of tPolygon3f; 168 | 169 | 170 | type tTraceInfo = packed record 171 | Point: tVec4f; // Intersection point 172 | t: Single; // ray value of intersection point 173 | u, v: Single; // barycentric coordinates of intersection point 174 | iTriangle: Integer; // Triangle id of Face polygon 175 | end; // 32 Bytes 176 | type PTraceInfo = ^tTraceInfo; 177 | type ATraceInfo = array of tTraceInfo; 178 | 179 | 180 | type tRGB888 = packed record 181 | r, g, b: Byte; 182 | end; 183 | type PRGB888 = ^tRGB888; 184 | type ARGB888 = array of tRGB888; 185 | 186 | type tRGBA8888 = packed record 187 | r, g, b, a: Byte; 188 | end; 189 | type PRGBA8888 = ^tRGBA8888; 190 | type ARGBA8888 = array of tRGBA8888; 191 | 192 | 193 | type tPixelIndexes = Array[0..32767] of Byte; 194 | type PPixelIndexes = ^tPixelIndexes; 195 | 196 | type pRGBArray = ^TRGBArray; 197 | TRGBArray = ARRAY[0..32767] OF TRGBTriple; 198 | 199 | type pRGBAArray = ^TRGBAArray; 200 | TRGBAArray = ARRAY[0..32767] OF TRGBQuad; 201 | 202 | 203 | type PByteBool = ^ByteBool; 204 | 205 | type APointer = array of Pointer; 206 | type AByte = array of Byte; 207 | type AInt = array of Integer; 208 | type ASingle = array of Single; 209 | type ADWORD = array of DWORD; 210 | type AWORD = array of WORD; 211 | type ASmallInt = array of SmallInt; 212 | type AByteBool = array of ByteBool; 213 | type AString = array of String; 214 | 215 | const 216 | inv16: Single = 0.0625; 217 | inv255: Single = 1.0/255.0; 218 | CR = #$0D; // = "/r" 219 | LF = #$0A; // = "/n" 220 | KEYBOARD_SHIFT = $10; 221 | // 222 | FLOAT32_INF_POSITIVE: Single = +1/0; 223 | FLOAT32_INF_NEGATIVE: Single = -1/0; 224 | VEC_ORT_X: tVec3f = (x: 1; y: 0; z: 0;); 225 | VEC_ORT_Y: tVec3f = (x: 0; y: 1; z: 0;); 226 | VEC_ORT_Z: tVec3f = (x: 0; y: 0; z: 1;); 227 | VEC_ZERO_2F: tVec2f = (x: 0; y: 0); 228 | VEC_ZERO_3F: tVec3f = (x: 0; y: 0; z: 0); 229 | VEC_ZERO_4F: tVec4f = (x: 0; y: 0; z: 0; w: 0); 230 | VEC_ONE: tVec3f = (x: 1; y: 1; z: 1); 231 | VEC_INF_P: tVec3f = (x: +1/0; y: +1/0; z: +1/0); 232 | VEC_INF_N: tVec3f = (x: -1/0; y: -1/0; z: -1/0); 233 | // 234 | VEC_ZERO_2B: tVec2b = (x: 0; y: 0); 235 | VEC2B_ORT_X: tVec2b = (x: 1; y: 0); 236 | VEC2B_ORT_Y: tVec2b = (x: 0; y: 1); 237 | // 238 | VEC_ZERO_2S: tVec2s = (x: 0; y: 0); 239 | // 240 | BBOX_ZERO_4F: tBBOX4f = (vMin: (x: 0; y: 0; z: 0; w: 0); vMax: (x: 0; y: 0; z: 0; w: 0)); 241 | TEX_BBOX_ZERO: tTexBBOXf = (vMin: (x: 0; y: 0); vMax: (x: 0; y: 0)); 242 | // 243 | RGB888_BLACK: tRGB888 = (r: 0; g: 0; b: 0); 244 | RGB888_WHITE: tRGB888 = (r: 255; g: 255; b: 255); 245 | RGBA8888_BLACK: tRGBA8888 = (r: 0; g: 0; b: 0; a: 255); 246 | RGBA8888_WHITE: tRGBA8888 = (r: 255; g: 255; b: 255; a: 255); 247 | RGBA8888_BLACK_TRANSPARENT: tRGBA8888 = (r: 0; g: 0; b: 0; a: 0); 248 | // 249 | // 250 | WhiteColor4f: tColor4fv = (1.0, 1.0, 1.0, 1.0); 251 | BlackColor4f: tColor4fv = (0.0, 0.0, 0.0, 1.0); 252 | RedColor4f: tColor4fv = (0.8, 0.1, 0.1, 0.5); 253 | PinkColor4f: tColor4fv = (1.0, 0.0, 1.0, 0.5); 254 | // 255 | ZerosMat3f: tMat3f = ( 256 | 0.0, 0.0, 0.0, 257 | 0.0, 0.0, 0.0, 258 | 0.0, 0.0, 0.0 259 | ); 260 | ZerosMat4f: tMat4f = ( 261 | 0.0, 0.0, 0.0, 0.0, 262 | 0.0, 0.0, 0.0, 0.0, 263 | 0.0, 0.0, 0.0, 0.0, 264 | 0.0, 0.0, 0.0, 0.0 265 | ); 266 | IdentityMat3f: tMat3f = ( 267 | 1.0, 0.0, 0.0, 268 | 0.0, 1.0, 0.0, 269 | 0.0, 0.0, 1.0 270 | ); 271 | IdentityMat4f: tMat4f = ( 272 | 1.0, 0.0, 0.0, 0.0, 273 | 0.0, 1.0, 0.0, 0.0, 274 | 0.0, 0.0, 1.0, 0.0, 275 | 0.0, 0.0, 0.0, 1.0 276 | ); 277 | // Plane axis type 278 | PLANE_X = 0; 279 | PLANE_Y = 1; 280 | PLANE_Z = 2; 281 | PLANE_ANY_X = 3; 282 | PLANE_ANY_Y = 4; 283 | PLANE_ANY_Z = 5; 284 | // 285 | PLANEBSP_NULL: tPlane = ( 286 | Normal: (x: 0.0; y: 0.0; z: 0.0); 287 | Dist: 0.0; 288 | AxisType: PLANE_ANY_Z 289 | ); 290 | 291 | 292 | implementation 293 | 294 | 295 | end. 296 | -------------------------------------------------------------------------------- /Modules/UnitVec.pas: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sergey-KoRJiK/SourceEngineBSPditor/5be52ab21baeea56188fb688e73041e4b2fa2ebd/Modules/UnitVec.pas -------------------------------------------------------------------------------- /Modules/UnitVertexBufferArrayManager.pas: -------------------------------------------------------------------------------- 1 | unit UnitVertexBufferArrayManager; 2 | 3 | // Copyright (c) 2020 Sergey-KoRJiK, Belarus 4 | // github.com/Sergey-KoRJiK 5 | 6 | interface 7 | 8 | uses 9 | SysUtils, 10 | Windows, 11 | Classes, 12 | OpenGL, 13 | UnitOpenGLext; 14 | 15 | const MaxCountVertexAttributes = 16; 16 | 17 | type tFillAttribInfo = record 18 | Size: GLint; 19 | AttribType: GLenum; 20 | bNormalized: GLboolean; 21 | Stride: GLsizei; 22 | Offset: PGLvoid; 23 | Divisor: GLuint; 24 | end; 25 | type PFillAttribInfo = ^tFillAttribInfo; 26 | type AFillAttribInfo = array of tFillAttribInfo; 27 | 28 | type CVertexBufferArrayManager = class 29 | private 30 | iVAO : GLuint; 31 | iCountAttribs : Integer; 32 | wFillAttribBits : Word; 33 | bIsCreated : Boolean; 34 | bIsAllAttribs : Boolean; 35 | public 36 | property IsCreated: Boolean read bIsCreated; 37 | property IsFillAllAttribs: Boolean read bIsAllAttribs; 38 | property CountAttributes: Integer read iCountAttribs; 39 | property BitFieldFillAttribs: Word read wFillAttribBits; 40 | // 41 | constructor CreateManager(); 42 | destructor DeleteManager(); 43 | // 44 | procedure Clear(); 45 | // 46 | function CreateAndFillVertexArray(const FillAttribInfoList: PFillAttribInfo; 47 | const VBOList: PGLuint; const CountAttribs: Integer): Boolean; 48 | function CreateNullVertexArray(const CountAttribs: Integer): Boolean; 49 | function AddArrayAttrib(const AttribIndex: Integer; 50 | const Info: tFillAttribInfo): Boolean; overload; 51 | function AddArrayAttrib(const AttribIndex: Integer; 52 | const Size: GLint; const AttribType: GLenum; const bNormalized: GLboolean; 53 | const Stride: GLint; const Offset: PGLVoid; 54 | const Divisor: GLuint): Boolean; overload; 55 | // 56 | function BindVertexArray(): Boolean; 57 | procedure UnbindVertexArray(); 58 | end; 59 | 60 | 61 | implementation 62 | 63 | 64 | const wFillAttribBitMask: array[0..15] of Word = ( 65 | $0001, $0003, $0007, $000F, 66 | $001F, $003F, $007F, $00FF, 67 | $01FF, $03FF, $07FF, $0FFF, 68 | $1FFF, $3FFF, $7FFF, $FFFF 69 | ); 70 | 71 | constructor CVertexBufferArrayManager.CreateManager(); 72 | begin 73 | {$R-} 74 | Self.iVAO:=0; 75 | Self.iCountAttribs:=0; 76 | Self.bIsCreated:=False; 77 | Self.bIsAllAttribs:=False; 78 | Self.wFillAttribBits:=$0000; 79 | {$R+} 80 | end; 81 | 82 | destructor CVertexBufferArrayManager.DeleteManager(); 83 | begin 84 | {$R-} 85 | Self.Clear(); 86 | {$R+} 87 | end; 88 | 89 | 90 | procedure CVertexBufferArrayManager.Clear(); 91 | begin 92 | {$R-} 93 | glDeleteVertexArrays(1, @Self.iVAO); 94 | Self.iCountAttribs:=0; 95 | Self.bIsCreated:=False; 96 | Self.bIsAllAttribs:=False; 97 | Self.wFillAttribBits:=$0000; 98 | {$R+} 99 | end; 100 | 101 | function CVertexBufferArrayManager.CreateAndFillVertexArray( 102 | const FillAttribInfoList: PFillAttribInfo; const VBOList: PGLuint; 103 | const CountAttribs: Integer): Boolean; 104 | var 105 | i: Integer; 106 | begin 107 | {$R-} 108 | Self.Clear(); 109 | 110 | Self.iCountAttribs:=CountAttribs; 111 | if (Self.iCountAttribs < 0) then 112 | begin 113 | Self.iCountAttribs:=0; 114 | end; 115 | if (Self.iCountAttribs >= MaxCountVertexAttributes) then 116 | begin 117 | Self.iCountAttribs:=MaxCountVertexAttributes - 1; 118 | end; 119 | 120 | glGenVertexArrays(1, @Self.iVAO); 121 | if (Self.iVAO = 0) then 122 | begin 123 | Self.Clear(); 124 | Result:=False; 125 | Exit; 126 | end; 127 | 128 | glBindVertexArray(Self.iVAO); 129 | 130 | for i:=0 to (Self.iCountAttribs - 1) do 131 | begin 132 | glBindBufferARB(GL_ARRAY_BUFFER, AGLuint(VBOList)[i]); 133 | glEnableVertexAttribArrayARB(i); 134 | case (AFillAttribInfo(FillAttribInfoList)[i].AttribType) of 135 | GL_FLOAT, GL_HALF_FLOAT, 136 | GL_FIXED, GL_INT_2_10_10_10_REV, 137 | GL_UNSIGNED_INT_2_10_10_10_REV, 138 | GL_UNSIGNED_INT_10F_11F_11F_REV: 139 | glVertexAttribPointerARB( 140 | i, 141 | AFillAttribInfo(FillAttribInfoList)[i].Size, 142 | AFillAttribInfo(FillAttribInfoList)[i].AttribType, 143 | AFillAttribInfo(FillAttribInfoList)[i].bNormalized, 144 | AFillAttribInfo(FillAttribInfoList)[i].Stride, 145 | AFillAttribInfo(FillAttribInfoList)[i].Offset 146 | ); 147 | // 148 | GL_BYTE, GL_UNSIGNED_BYTE, 149 | GL_SHORT, GL_UNSIGNED_SHORT, 150 | GL_INT, GL_UNSIGNED_INT: 151 | glVertexAttribIPointer( 152 | i, 153 | AFillAttribInfo(FillAttribInfoList)[i].Size, 154 | AFillAttribInfo(FillAttribInfoList)[i].AttribType, 155 | AFillAttribInfo(FillAttribInfoList)[i].Stride, 156 | AFillAttribInfo(FillAttribInfoList)[i].Offset 157 | ); 158 | // 159 | GL_DOUBLE: 160 | glVertexAttribLPointer( 161 | i, 162 | AFillAttribInfo(FillAttribInfoList)[i].Size, 163 | GL_DOUBLE, 164 | AFillAttribInfo(FillAttribInfoList)[i].Stride, 165 | AFillAttribInfo(FillAttribInfoList)[i].Offset 166 | ); 167 | end; 168 | glVertexAttribDivisorARB(i, AFillAttribInfo(FillAttribInfoList)[i].Divisor); 169 | Self.wFillAttribBits:=Self.wFillAttribBits or (1 shl i); 170 | end; 171 | 172 | glBindBufferARB(GL_ARRAY_BUFFER, 0); 173 | glBindVertexArray(0); //} 174 | 175 | Self.bIsCreated:=True; 176 | Self.bIsAllAttribs:=True; 177 | Result:=True; 178 | {$R+} 179 | end; 180 | 181 | function CVertexBufferArrayManager.CreateNullVertexArray( 182 | const CountAttribs: Integer): Boolean; 183 | begin 184 | {$R-} 185 | Self.Clear(); 186 | 187 | Self.iCountAttribs:=CountAttribs; 188 | if (Self.iCountAttribs < 0) then 189 | begin 190 | Self.iCountAttribs:=0; 191 | end; 192 | if (Self.iCountAttribs >= MaxCountVertexAttributes) then 193 | begin 194 | Self.iCountAttribs:=MaxCountVertexAttributes - 1; 195 | end; 196 | 197 | glGenVertexArrays(1, @Self.iVAO); 198 | if (Self.iVAO = 0) then 199 | begin 200 | Self.Clear(); 201 | Result:=False; 202 | Exit; 203 | end; 204 | 205 | Self.bIsCreated:=True; 206 | Result:=True; 207 | {$R+} 208 | end; 209 | 210 | function CVertexBufferArrayManager.AddArrayAttrib(const AttribIndex: Integer; 211 | const Info: tFillAttribInfo): Boolean; 212 | begin 213 | {$R-} 214 | if ((Self.bIsCreated = False) or (Self.bIsAllAttribs) 215 | or (AttribIndex < 0) or (AttribIndex >= Self.iCountAttribs)) then 216 | begin 217 | Result:=False; 218 | Exit; 219 | end; 220 | 221 | if ((Self.wFillAttribBits and (1 shl AttribIndex)) <> 0) then 222 | begin 223 | // Attrib already filled 224 | Result:=False; 225 | Exit; 226 | end; 227 | 228 | glEnableVertexAttribArrayARB(AttribIndex); 229 | case (Info.AttribType) of 230 | GL_FLOAT, GL_HALF_FLOAT, GL_FIXED, 231 | GL_INT_2_10_10_10_REV, GL_UNSIGNED_INT_2_10_10_10_REV, 232 | GL_UNSIGNED_INT_10F_11F_11F_REV: glVertexAttribPointerARB( 233 | AttribIndex, 234 | Info.Size, 235 | Info.AttribType, 236 | Info.bNormalized, 237 | Info.Stride, 238 | Info.Offset 239 | ); 240 | // 241 | GL_BYTE, GL_UNSIGNED_BYTE, 242 | GL_SHORT, GL_UNSIGNED_SHORT, 243 | GL_INT, GL_UNSIGNED_INT: glVertexAttribIPointer( 244 | AttribIndex, 245 | Info.Size, 246 | Info.AttribType, 247 | Info.Stride, 248 | Info.Offset 249 | ); 250 | // 251 | GL_DOUBLE: glVertexAttribLPointer( 252 | AttribIndex, 253 | Info.Size, 254 | GL_DOUBLE, 255 | Info.Stride, 256 | Info.Offset 257 | ); 258 | end; 259 | 260 | glVertexAttribDivisorARB(AttribIndex, Info.Divisor); 261 | Self.wFillAttribBits:=Self.wFillAttribBits or (1 shl AttribIndex); 262 | 263 | Self.bIsAllAttribs:=Boolean(Self.wFillAttribBits = wFillAttribBitMask[Self.iCountAttribs]); 264 | Result:=True; 265 | {$R+} 266 | end; 267 | 268 | function CVertexBufferArrayManager.AddArrayAttrib(const AttribIndex: Integer; 269 | const Size: GLint; const AttribType: GLenum; const bNormalized: GLboolean; 270 | const Stride: GLint; const Offset: PGLVoid; const Divisor: GLuint): Boolean; 271 | begin 272 | {$R-} 273 | if ((Self.bIsCreated = False) or (Self.bIsAllAttribs) 274 | or (AttribIndex < 0) or (AttribIndex >= Self.iCountAttribs)) then 275 | begin 276 | Result:=False; 277 | Exit; 278 | end; 279 | 280 | if ((Self.wFillAttribBits and (1 shl AttribIndex)) <> 0) then 281 | begin 282 | // Attrib already filled 283 | Result:=False; 284 | Exit; 285 | end; 286 | 287 | glEnableVertexAttribArrayARB(AttribIndex); 288 | case (AttribType) of 289 | GL_FLOAT, GL_HALF_FLOAT, GL_FIXED, 290 | GL_INT_2_10_10_10_REV, GL_UNSIGNED_INT_2_10_10_10_REV, 291 | GL_UNSIGNED_INT_10F_11F_11F_REV: glVertexAttribPointerARB( 292 | AttribIndex, 293 | Size, 294 | AttribType, 295 | bNormalized, 296 | Stride, 297 | Offset 298 | ); 299 | // 300 | GL_BYTE, GL_UNSIGNED_BYTE, 301 | GL_SHORT, GL_UNSIGNED_SHORT, 302 | GL_INT, GL_UNSIGNED_INT: glVertexAttribIPointer( 303 | AttribIndex, 304 | Size, 305 | AttribType, 306 | Stride, 307 | Offset 308 | ); 309 | // 310 | GL_DOUBLE: glVertexAttribLPointer( 311 | AttribIndex, 312 | Size, 313 | GL_DOUBLE, 314 | Stride, 315 | Offset 316 | ); 317 | end; 318 | 319 | glVertexAttribDivisorARB(AttribIndex, Divisor); 320 | Self.wFillAttribBits:=Self.wFillAttribBits or (1 shl AttribIndex); 321 | 322 | Self.bIsAllAttribs:=Boolean(Self.wFillAttribBits = wFillAttribBitMask[Self.iCountAttribs]); 323 | Result:=True; 324 | {$R+} 325 | end; 326 | 327 | function CVertexBufferArrayManager.BindVertexArray(): Boolean; 328 | begin 329 | {$R-} 330 | if (Self.bIsCreated) then glBindVertexArray(Self.iVAO); 331 | Result:=Self.bIsCreated; 332 | {$R+} 333 | end; 334 | 335 | procedure CVertexBufferArrayManager.UnbindVertexArray(); 336 | begin 337 | {$R-} 338 | if (Self.bIsCreated) then glBindVertexArray(0); 339 | {$R+} 340 | end; 341 | 342 | end. 343 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019-2025 Sergey-KoRJiK, Belarus 2 | 3 | # This programm created for View and Edit Source Engine BSP maps. 4 | - Lightmap atlas preview, per-pixel preview, save to file styles and bumps 5 | - leaf BBOX preview 6 | - HDR\LDR tonemap exposure slider 7 | - hide\show triggers, BModels 8 | - Face select info (vertices, plane, disp, texinfo) 9 | - dev build 1.1.1 include only lightmap & leaf preview, no ambient cubes now 10 | ![](title.png) 11 | 12 | -------------------------------------------------------------------------------- /SourceBSPViewer.cfg: -------------------------------------------------------------------------------- 1 | -$A8 2 | -$B- 3 | -$C+ 4 | -$D+ 5 | -$E- 6 | -$F- 7 | -$G+ 8 | -$H+ 9 | -$I+ 10 | -$J- 11 | -$K- 12 | -$L+ 13 | -$M- 14 | -$N+ 15 | -$O+ 16 | -$P+ 17 | -$Q- 18 | -$R- 19 | -$S- 20 | -$T- 21 | -$U- 22 | -$V+ 23 | -$W- 24 | -$X+ 25 | -$YD 26 | -$Z1 27 | -cg 28 | -AWinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; 29 | -H+ 30 | -W+ 31 | -M 32 | -$M16384,1048576 33 | -K$00400000 34 | -LE"c:\program files (x86)\borland\delphi7\Projects\Bpl" 35 | -LN"c:\program files (x86)\borland\delphi7\Projects\Bpl" 36 | -w-UNSAFE_TYPE 37 | -w-UNSAFE_CODE 38 | -w-UNSAFE_CAST 39 | -------------------------------------------------------------------------------- /SourceBSPViewer.dof: -------------------------------------------------------------------------------- 1 | [FileVersion] 2 | Version=7.0 3 | [Compiler] 4 | A=8 5 | B=0 6 | C=1 7 | D=1 8 | E=0 9 | F=0 10 | G=1 11 | H=1 12 | I=1 13 | J=0 14 | K=0 15 | L=1 16 | M=0 17 | N=1 18 | O=1 19 | P=1 20 | Q=0 21 | R=0 22 | S=0 23 | T=0 24 | U=0 25 | V=1 26 | W=0 27 | X=1 28 | Y=1 29 | Z=1 30 | ShowHints=1 31 | ShowWarnings=1 32 | UnitAliases=WinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; 33 | NamespacePrefix= 34 | SymbolDeprecated=1 35 | SymbolLibrary=1 36 | SymbolPlatform=1 37 | UnitLibrary=1 38 | UnitPlatform=1 39 | UnitDeprecated=1 40 | HResultCompat=1 41 | HidingMember=1 42 | HiddenVirtual=1 43 | Garbage=1 44 | BoundsError=1 45 | ZeroNilCompat=1 46 | StringConstTruncated=1 47 | ForLoopVarVarPar=1 48 | TypedConstVarPar=1 49 | AsgToTypedConst=1 50 | CaseLabelRange=1 51 | ForVariable=1 52 | ConstructingAbstract=1 53 | ComparisonFalse=1 54 | ComparisonTrue=1 55 | ComparingSignedUnsigned=1 56 | CombiningSignedUnsigned=1 57 | UnsupportedConstruct=1 58 | FileOpen=1 59 | FileOpenUnitSrc=1 60 | BadGlobalSymbol=1 61 | DuplicateConstructorDestructor=1 62 | InvalidDirective=1 63 | PackageNoLink=1 64 | PackageThreadVar=1 65 | ImplicitImport=1 66 | HPPEMITIgnored=1 67 | NoRetVal=1 68 | UseBeforeDef=1 69 | ForLoopVarUndef=1 70 | UnitNameMismatch=1 71 | NoCFGFileFound=1 72 | MessageDirective=1 73 | ImplicitVariants=1 74 | UnicodeToLocale=1 75 | LocaleToUnicode=1 76 | ImagebaseMultiple=1 77 | SuspiciousTypecast=1 78 | PrivatePropAccessor=1 79 | UnsafeType=0 80 | UnsafeCode=0 81 | UnsafeCast=0 82 | [Linker] 83 | MapFile=0 84 | OutputObjs=0 85 | ConsoleApp=1 86 | DebugInfo=0 87 | RemoteSymbols=0 88 | MinStackSize=16384 89 | MaxStackSize=1048576 90 | ImageBase=4194304 91 | ExeDescription= 92 | [Directories] 93 | OutputDir= 94 | UnitOutputDir= 95 | PackageDLLOutputDir= 96 | PackageDCPOutputDir= 97 | SearchPath= 98 | Packages=vcl;rtl;vclx;indy;inet;xmlrtl;vclie;inetdbbde;inetdbxpress;dbrtl;dsnap;dsnapcon;vcldb;soaprtl;VclSmp;dbexpress;dbxcds;inetdb;bdertl;vcldbx;webdsnap;websnap;adortl;ibxpress;teeui;teedb;tee;dss;visualclx;visualdbclx;vclactnband;vclshlctrls;IntrawebDB_50_70;Intraweb_50_70;Rave50CLX;Rave50VCL;dclOfficeXP 99 | Conditionals= 100 | DebugSourceDirs= 101 | UsePackages=0 102 | [Parameters] 103 | RunParams= 104 | HostApplication= 105 | Launcher= 106 | UseLauncher=0 107 | DebugCWD= 108 | [Language] 109 | ActiveLang= 110 | ProjectLang= 111 | RootDir= 112 | [Version Info] 113 | IncludeVerInfo=1 114 | AutoIncBuild=0 115 | MajorVer=1 116 | MinorVer=4 117 | Release=1 118 | Build=1 119 | Debug=0 120 | PreRelease=1 121 | Special=0 122 | Private=0 123 | DLL=0 124 | Locale=1033 125 | CodePage=1252 126 | [Version Info Keys] 127 | CompanyName= 128 | FileDescription=Source BSP Editor and Viewer 129 | FileVersion=1.4.1.1 130 | InternalName= 131 | LegalCopyright=Sergey-KoRJiK 132 | LegalTrademarks= 133 | OriginalFilename= 134 | ProductName=Source BSP Editor and Viewer 135 | ProductVersion=1.4.1.1 136 | Comments= 137 | -------------------------------------------------------------------------------- /SourceBSPViewer.dpr: -------------------------------------------------------------------------------- 1 | program SourceBSPViewer; 2 | 3 | // Copyright (c) 2020 Sergey-KoRJiK, Belarus 4 | 5 | uses 6 | Forms, 7 | Unit1 in 'Unit1.pas' {MainForm}, 8 | UnitUserTypes in 'Modules\UnitUserTypes.pas', 9 | UnitVec in 'Modules\UnitVec.pas', 10 | UnitOpenGLext in 'Modules\UnitOpenGLext.pas', 11 | UnitOpenGLErrorManager in 'Modules\UnitOpenGLErrorManager.pas', 12 | UnitOpenGLFPSCamera in 'Modules\UnitOpenGLFPSCamera.pas', 13 | UnitRenderingContextManager in 'Modules\UnitRenderingContextManager.pas', 14 | UnitShaderManager in 'Modules\UnitShaderManager.pas', 15 | UnitVertexBufferArrayManager in 'Modules\UnitVertexBufferArrayManager.pas', 16 | UnitMegatextureManager in 'Modules\UnitMegatextureManager.pas', 17 | UnitQueryPerformanceTimer in 'Modules\UnitQueryPerformanceTimer.pas', 18 | UnitRenderTimerManager in 'Modules\UnitRenderTimerManager.pas', 19 | UnitBSPstruct in 'Modules\SourceBSP\UnitBSPstruct.pas', 20 | UnitEntity in 'Modules\SourceBSP\UnitEntity.pas', 21 | UnitRescaling2D in 'Modules\UnitRescaling2D.pas', 22 | UnitQueryPerformanceProfiler in 'Modules\UnitQueryPerformanceProfiler.pas', 23 | UnitImageTGA in 'Modules\UnitImageTGA.pas', 24 | UnitRadianceHDR in 'Modules\UnitRadianceHDR.pas', 25 | UnitVTFMeowLib in 'Modules\UnitVTFMeowLib.pas', 26 | UnitBufferObjectManager in 'Modules\UnitBufferObjectManager.pas', 27 | UnitDispMisc in 'Modules\SourceBSP\UnitDispMisc.pas'; 28 | 29 | {$R *.res} 30 | var 31 | MainForm: TMainForm; 32 | 33 | begin 34 | Application.Initialize; 35 | Application.Title := 'Source BSP Editor'; 36 | Application.CreateForm(TMainForm, MainForm); 37 | Application.Run; 38 | end. 39 | -------------------------------------------------------------------------------- /SourceBSPViewer.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sergey-KoRJiK/SourceEngineBSPditor/5be52ab21baeea56188fb688e73041e4b2fa2ebd/SourceBSPViewer.exe -------------------------------------------------------------------------------- /SourceBSPViewer.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sergey-KoRJiK/SourceEngineBSPditor/5be52ab21baeea56188fb688e73041e4b2fa2ebd/SourceBSPViewer.res -------------------------------------------------------------------------------- /SourceEngineMapViewer.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sergey-KoRJiK/SourceEngineBSPditor/5be52ab21baeea56188fb688e73041e4b2fa2ebd/SourceEngineMapViewer.exe -------------------------------------------------------------------------------- /Unit1.ddp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sergey-KoRJiK/SourceEngineBSPditor/5be52ab21baeea56188fb688e73041e4b2fa2ebd/Unit1.ddp -------------------------------------------------------------------------------- /changes.txt: -------------------------------------------------------------------------------- 1 | Version 1.0.0: 2 | first build 3 | 4 | Version 1.0.1: 5 | add support HDR Lightmaps 6 | add change HDR\LDR by key "G" 7 | delete global HDR Ratio 8 | add HDR Ratio for each styles and HDR\LDR Lightmaps 9 | fix camera speed (new timer) 10 | fix null Lightmaps TexCoord array for displaments 11 | 12 | Versiom 1.0.2: 13 | fix problems 14 | 15 | Versiom 1.0.3: 16 | Add editor of Leaf Ambient Lighting: Color and Position 17 | Add simple reconstrution of Ambient light at any position in gamma space 18 | Fixed change gamma correction for lightmap at 1/2.2 19 | 20 | ReVersion 1.1.0: 21 | Total revision from zero. Version 1.1.0 only map view (no edit) 22 | Render upgrade to OpenGL 3.3 with shaders. HDR\LDR tonemap 23 | Lightmap atlas 1024x1024, Atlas preview 24 | Lightmap exposure realtime slider 25 | Entity BModel\Trigger draw toggle 26 | Face select info 27 | PVS of clusters instead of leafs 28 | light styles preview (without accum) 29 | BBOX leaf preview. 30 | No displacement (in process) 31 | -------------------------------------------------------------------------------- /shaders/CubeFRAG.cpp: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | in vec4 fLineColor; 4 | out vec4 outColor; 5 | 6 | // bbox draw 7 | void main() 8 | { 9 | outColor = vec4(fLineColor); 10 | } -------------------------------------------------------------------------------- /shaders/CubeVERT.cpp: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | layout (location = 0) in vec4 aPos; 4 | 5 | out vec4 fLineColor; 6 | 7 | uniform mat4 cameramat; // camera matrix 8 | uniform vec3 bbmin; 9 | uniform vec3 bbmax; 10 | uniform vec4 fcolor; 11 | 12 | void main() 13 | { 14 | fLineColor = fcolor; 15 | 16 | vec3 tmp = (bbmax - bbmin)*(aPos.xyz + vec3(1.0f))*0.5f + bbmin; 17 | 18 | gl_Position = cameramat * vec4(tmp, 1.0); 19 | //gl_Position = cameramat * vec4(aPos.xyz, 1.0); 20 | } -------------------------------------------------------------------------------- /shaders/DispFRAG.cpp: -------------------------------------------------------------------------------- 1 | #version 330 2 | precision highp float; 3 | 4 | in vec2 fTexCoord; 5 | in vec2 fLmpCoord; 6 | in float fDispAlpha; 7 | 8 | out vec4 outColor; 9 | 10 | uniform sampler2D sampler0; // lightmap 11 | uniform vec4 fxColorAlpha; // FX Engine 12 | uniform float fStyle; 13 | uniform float fBump; 14 | uniform float fLightmapExpShift; 15 | uniform vec4 fLmpCAPS; // xy = Lmp size in Atlas coord, z = max bumps, w = max styles 16 | 17 | const float fLightmapExpBais = 128.0f / 255.0f; 18 | const vec3 fInvGamma = vec3(1.0f / 2.2f); 19 | const vec4 FULLBRIGHT = vec4(0.0f, 0.0f, 0.0f, 1.0f); 20 | 21 | vec3 UnpackLightmap(vec4 lmp) { 22 | // "Integer HDR" 23 | float preExp = (lmp.a - fLightmapExpBais + fLightmapExpShift)*255.0f; 24 | return vec3( lmp.rgb * exp2(preExp) ); 25 | } 26 | 27 | // World and entity BModel Faces 28 | void main() 29 | { 30 | vec3 color_final; 31 | 32 | vec2 fStyleBump = vec2(0.0f); 33 | if (fBump < fLmpCAPS.z) {fStyleBump.x = fLmpCAPS.x*fBump;} 34 | if (fStyle < fLmpCAPS.w) {fStyleBump.y = fLmpCAPS.y*fStyle;} 35 | 36 | vec4 lmp = texture(sampler0, fLmpCoord).rgba; 37 | if (FULLBRIGHT == lmp) { 38 | color_final = fxColorAlpha.rgb; 39 | } else { 40 | vec3 lmp_swtich = vec3(0.0f); 41 | if (fStyleBump.y > 0) lmp_swtich = UnpackLightmap(texture(sampler0, fLmpCoord + fStyleBump).rgba); 42 | 43 | vec3 lmp_final = UnpackLightmap(lmp) + lmp_swtich; 44 | color_final = lmp_final * fxColorAlpha.rgb; 45 | 46 | color_final = pow(color_final, fInvGamma); 47 | // color_final /= (color_final + vec3(1.0f)); // tonemap "x/(x+1)" 48 | } 49 | 50 | outColor = vec4(color_final, fxColorAlpha.a); 51 | } -------------------------------------------------------------------------------- /shaders/DispVERT.cpp: -------------------------------------------------------------------------------- 1 | #version 330 2 | precision highp float; 3 | 4 | layout (location = 0) in vec4 aPos; 5 | 6 | out vec2 fTexCoord; 7 | out vec2 fLmpCoord; 8 | out float fDispAlpha; 9 | 10 | uniform mat4 cameramat; 11 | uniform vec3 entOrigin; 12 | uniform vec3 entAngles; // XYZ = Pitch Yaw Roll 13 | uniform vec4 TexLmp0; 14 | uniform vec4 TexLmp1; 15 | uniform vec4 TexLmp2; 16 | uniform vec4 TexLmp3; 17 | uniform int iDispSize; 18 | uniform int iDispBase; 19 | uniform int iBaseVertex; 20 | 21 | vec2 TessellateFlatVec2(in vec2 v0, in vec2 v1, in vec2 v2, in vec2 v3, in vec2 f, in int iSwizzle) { 22 | vec2 res = v0; 23 | vec2 tmpA, tmpB; 24 | /*switch (iSwizzle % 4) { 25 | case 0: 26 | tmpA = (1 - f.x)*v3 + f.x*v0; 27 | tmpB = (1 - f.x)*v2 + f.x*v1; 28 | res = (1 - f.y)*tmpA + f.y*tmpB; 29 | break; 30 | case 1: 31 | tmpA = (1 - f.x)*v1 + f.x*v2; 32 | tmpB = (1 - f.x)*v0 + f.x*v3; 33 | res = (1 - f.y)*tmpB + f.y*tmpA; 34 | break; 35 | case 2: 36 | tmpA = (1 - f.x)*v2 + f.x*v3; 37 | tmpB = (1 - f.x)*v1 + f.x*v0; 38 | res = (1 - f.y)*tmpA + f.y*tmpB; 39 | break; 40 | case 3: 41 | tmpA = (1 - f.x)*v2 + f.x*v1; 42 | tmpB = (1 - f.x)*v3 + f.x*v0; 43 | res = (1 - f.y)*tmpA + f.y*tmpB; 44 | break; 45 | } //*/ 46 | 47 | tmpA = (1 - f.x)*v0 + f.x*v3; 48 | tmpB = (1 - f.x)*v1 + f.x*v2; 49 | res = (1 - f.y)*tmpA + f.y*tmpB; //*/ 50 | 51 | return res; 52 | } 53 | 54 | void main() 55 | { 56 | vec2 fFrac = vec2( int((gl_VertexID-iBaseVertex) / iDispSize), mod((gl_VertexID-iBaseVertex), iDispSize) ); 57 | fFrac /= float(iDispSize-1); 58 | fTexCoord = TessellateFlatVec2(TexLmp0.xy, TexLmp1.xy, TexLmp2.xy, TexLmp3.xy, fFrac, iDispBase); 59 | fLmpCoord = TessellateFlatVec2(TexLmp0.zw, TexLmp1.zw, TexLmp2.zw, TexLmp3.zw, fFrac.yx, iDispBase); 60 | fDispAlpha = aPos.w; 61 | 62 | gl_Position = cameramat * vec4(aPos.xyz, 1.0); 63 | //gl_Position = vec4(aPos, 1.0); 64 | } -------------------------------------------------------------------------------- /shaders/FaceFRAG.cpp: -------------------------------------------------------------------------------- 1 | #version 330 2 | precision highp float; 3 | 4 | in vec2 fTexCoord; 5 | in vec2 fLmpCoord; 6 | in vec4 fLmpCAPS; // xy = Lmp size in Atlas coord, z = max bumps, w = max styles 7 | 8 | out vec4 outColor; 9 | 10 | uniform sampler2D sampler0; // lightmap 11 | uniform vec4 fxColorAlpha; // FX Engine 12 | uniform float fStyle; 13 | uniform float fBump; 14 | uniform float fLightmapExpShift; 15 | 16 | const float fLightmapExpBais = 128.0f / 255.0f; 17 | const vec3 fInvGamma = vec3(1.0f / 2.2f); 18 | const vec4 FULLBRIGHT = vec4(0.0f, 0.0f, 0.0f, 1.0f); 19 | 20 | vec3 UnpackLightmap(vec4 lmp) { 21 | // "Integer HDR" 22 | float preExp = (lmp.a - fLightmapExpBais + fLightmapExpShift)*255.0f; 23 | return vec3( lmp.rgb * exp2(preExp) ); 24 | } 25 | 26 | // World and entity BModel Faces 27 | void main() 28 | { 29 | vec3 color_final; 30 | 31 | vec2 fStyleBump = vec2(0.0f); 32 | if (fBump < fLmpCAPS.z) {fStyleBump.x = fLmpCAPS.x*fBump;} 33 | if (fStyle < fLmpCAPS.w) {fStyleBump.y = fLmpCAPS.y*fStyle;} 34 | 35 | vec4 lmp = texture(sampler0, fLmpCoord).rgba; 36 | if (FULLBRIGHT == lmp) { 37 | color_final = fxColorAlpha.rgb; 38 | } else { 39 | vec3 lmp_swtich = vec3(0.0f); 40 | if (fStyleBump.y > 0) lmp_swtich = UnpackLightmap(texture(sampler0, fLmpCoord + fStyleBump).rgba); 41 | 42 | vec3 lmp_final = UnpackLightmap(lmp) + lmp_swtich; 43 | color_final = lmp_final * fxColorAlpha.rgb; 44 | 45 | color_final = pow(color_final, fInvGamma); 46 | // color_final /= (color_final + vec3(1.0f)); // tonemap "x/(x+1)" 47 | } 48 | 49 | outColor = vec4(color_final, fxColorAlpha.a); 50 | } -------------------------------------------------------------------------------- /shaders/FaceVERT.cpp: -------------------------------------------------------------------------------- 1 | #version 330 2 | precision highp float; 3 | 4 | layout (location = 0) in vec3 aPos; 5 | layout (location = 1) in vec2 aTexCoord; 6 | layout (location = 2) in vec2 aLmpCoord; 7 | layout (location = 3) in vec4 aLmpCAPS; 8 | 9 | out vec2 fTexCoord; 10 | out vec2 fLmpCoord; 11 | out vec4 fLmpCAPS; 12 | 13 | uniform mat4 cameramat; 14 | uniform vec3 entOrigin; 15 | uniform vec3 entAngles; // XYZ = Pitch Yaw Roll 16 | 17 | void main() 18 | { 19 | fTexCoord = aTexCoord; 20 | fLmpCoord = aLmpCoord; 21 | fLmpCAPS = aLmpCAPS; 22 | 23 | gl_Position = cameramat * vec4(aPos.xyz + entOrigin.xyz, 1.0); 24 | //gl_Position = vec4(aPos, 1.0); 25 | } -------------------------------------------------------------------------------- /shaders/OrtsFRAG.cpp: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | in vec3 fLineColor; 4 | out vec4 outColor; 5 | 6 | // orthonormal XYZ draw 7 | void main() 8 | { 9 | outColor = vec4(fLineColor, 1.0f); 10 | } -------------------------------------------------------------------------------- /shaders/OrtsVERT.cpp: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | layout (location = 0) in vec3 aPos; 4 | layout (location = 1) in vec3 aLineColor; 5 | 6 | out vec3 fLineColor; 7 | 8 | uniform mat4 cameramat; // camera matrix 9 | uniform float ortscale; 10 | 11 | void main() 12 | { 13 | fLineColor = aLineColor; 14 | gl_Position = cameramat * vec4(aPos.xyz*ortscale, 1.0); 15 | } -------------------------------------------------------------------------------- /shaders/SFaceFRAG.cpp: -------------------------------------------------------------------------------- 1 | #version 330 2 | precision highp float; 3 | 4 | out vec4 outColor; 5 | 6 | uniform vec4 fSelColor; 7 | 8 | // face select 9 | void main() 10 | { 11 | outColor = vec4(fSelColor); 12 | } -------------------------------------------------------------------------------- /shaders/SFaceVERT.cpp: -------------------------------------------------------------------------------- 1 | #version 330 2 | precision highp float; 3 | 4 | layout (location = 0) in vec3 aPos; 5 | layout (location = 1) in vec2 aTexCoord; 6 | layout (location = 2) in vec2 aLmpCoord; 7 | 8 | uniform mat4 cameramat; 9 | 10 | uniform vec3 fOffset; // "Face plane normal"*1.0 + "BModel origin" 11 | 12 | void main() 13 | { 14 | gl_Position = cameramat * vec4(aPos.xyz + fOffset.xyz, 1.0); 15 | //gl_Position = vec4(aPos, 1.0); 16 | } -------------------------------------------------------------------------------- /title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sergey-KoRJiK/SourceEngineBSPditor/5be52ab21baeea56188fb688e73041e4b2fa2ebd/title.png --------------------------------------------------------------------------------