Formats.LWO.pas 59 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468
  1. //
  2. // The graphics engine GLScene https://github.com/glscene
  3. //
  4. unit Formats.LWO;
  5. (* =============================================================
  6. This unit provides functions, constants and now classes for use in
  7. working with Lightwave3D Object files.
  8. Chunk ID constants are defined for all of the Chunk IDs listed
  9. in the Lightwave 7.5 sdk.
  10. It is important to note that this is a constant work-in-progress
  11. and as such there are omissions and may be errors. Feedback and
  12. suggestions would be appreciated.
  13. There are two ways of using this unit. The first uses user-defines
  14. callbacks to handle parsing lwo chunk data. The second way uses
  15. object orientation.
  16. Loading LWO chunk data via callbacks
  17. A function is provided for loading a Lightwave object from a file.
  18. The Loader itself uses a callback mechanism for the loading of
  19. Lightwave object chunks. The callback is called for every chunk
  20. (with the exception of the FORM and LWOB or LWO2 chunks).
  21. The Chunk struct passed in the callback contains members for the
  22. chunk ID, chunk size and pointer to chunk data. This data is
  23. untouched internally so any parsing and numeric formatting
  24. is up to you. This provides maximum flexibility and allows you to
  25. handle the data that you need without loading the entire object
  26. into ram first.
  27. The chunk data memory is freed upon the return of the callback
  28. so do not keep a reference to the chunk data. Copy it to your own
  29. storage.
  30. function LoadLW0(const Filename: string; ReadProc: TLWOReadProc;
  31. UserData: Pointer): LongWord; cdecl;
  32. Filename: The fully qualified filename of the file to be
  33. loaded.
  34. ReadCallback: The address of a TLWOReadCallback procedure
  35. defined as:
  36. TLWOReadCallback = procedure(Chunk: TLWChunk;
  37. UserData: Pointer); cdecl;
  38. This procedure will be called for every chunk
  39. encountered in the Lightwave object file. The
  40. Chunk parameter is the chunk struct of the chunk
  41. being loaded. UserData is the pointer supplied
  42. in the original call to LoadLWO (see below).
  43. UserData: A pointer to user supplied data to be passed
  44. in the ReadCallback.
  45. A non-zero results indicates that the object file was parsed
  46. successfully.
  47. Loading LWO chunks via objects
  48. ============================
  49. To load data from a lightwave object file, create an instance of
  50. TLWObjectFile and call its LoadFromFile method.
  51. The data can then be accessed with the Chunks array property and
  52. iterated in combination with the ChunkCount property.
  53. Chunk data is parsed and interfaced by descendents of the TLWChunk
  54. class. I have made handlers for the following chunk types:
  55. TLWLayr Modeler Layer chunk
  56. TLWPnts Points chunk
  57. TLWPols Polygons chunk
  58. TLWPTag Polygon tag mapping
  59. TLWSurf Surface subchunk container
  60. TLWTags Tags (Name tag strings for named items)
  61. TLWVMap Vertex Mapping
  62. The data for chunks without handlers can be gotten at with the
  63. Data and Size properties of the TLWChunk. Data is a pointer to
  64. the start of the chunk data. This data is unparsed.
  65. Data is nil for descendents.
  66. This should provide enough to move geometry into your favourite
  67. delphi-based 3d engine.
  68. Making chunk handler objects
  69. ============================
  70. All chunk types are derived from TLWChunk in the following manner:
  71. TLWChunk
  72. ex:
  73. TLWPTag <- PTAG chunk type. polygon tag map.
  74. TLWParentChunk <- A base class for chunks that can contain other chunks.
  75. This is not necessarily how the data is stored in
  76. the file but more for ease of access to data.
  77. ex:
  78. TLWPnts <- PNTS chunk type (points)
  79. TLWLayr <- LAYR chunk type (modeler layer)
  80. TLWSurf <- SURF chunk type (constains surface attributes as sub chunks)
  81. TLWSubChunk <- A base class for chunks whose max data len is 65536 bytes.
  82. TLWDiff <- DIFF subchunk type (diffuse surface parameter)
  83. TLWSpec <- SPEC subchunk type (specularity surface parameter)...
  84. etc.
  85. Each descendent of TLWChunk or TLWSubChunk is required to override
  86. the GetID class function, the LoadData method and the Clear method
  87. to provide custom handling for chunktype data.
  88. ex:
  89. ...
  90. type
  91. TLWPnts = class (TLWParentChunk)
  92. private
  93. FPoints: TVEC12DynArray;
  94. function GetCount: LongWord;
  95. protected
  96. procedure Clear; override;
  97. procedure LoadData(AStream: TStream; DataStart, DataSize: LongWord); override;
  98. public
  99. class function GetID: TID4; override;
  100. function GetVMap(VMapID: TID4; out VMap: TLWVMap): boolean;
  101. property Count: LongWord read GetCount;
  102. property Points: TVEC12DynArray read FPoints;
  103. end;
  104. ...
  105. // Return the the chunk id that is the target of this handler
  106. class function TLWPnts.GetID: TID4;
  107. begin
  108. result := ID_PNTS;
  109. end;
  110. // Load the point data - the stream is already positioned at the start of the chunk data
  111. procedure TLWPnts.LoadData(AStream: TStream; DataStart, DataSize: LongWord);
  112. begin
  113. SetLength(FPoints,DataSize div 12); // allocate storage for DataSize div 12 points
  114. ReadMotorolaNumber(AStream,@FPoints[0],4,DataSize div 4); // read the point data
  115. end;
  116. // Cleanup - Free any memory that you've allocated
  117. procedure TLWPnts.Clear;
  118. begin
  119. SetLength(FPoints,0);
  120. end;
  121. Utility Functions
  122. =================
  123. A function is provided for converting an array of numbers between
  124. Motorola and Intel format (big endian <-> little endian). Converting
  125. only needs to be done for numeric types that are of 2 or 4 byte
  126. lengths.
  127. procedure ReverseByteOrder(ValueIn: Pointer; Size: integer; Count: integer = 1);
  128. ValueIn: The address of a number or array of numbers to have their
  129. bytes swapped.
  130. Size: The size in bytes of each numeric type.
  131. Count: The count of numbers in the numbers array. The default
  132. value is 1.
  133. Two routines are provided for reading and writing big endian
  134. (Motorola and misc other processor vendors ) numbers to and from a
  135. stream. These routines handle 2 and 4 byte numeric types and can
  136. also handle arrays.
  137. procedure ReadMotorolaNumber(Stream: TStream; Data: Pointer;
  138. ElementSize: integer; Count: integer = 1);
  139. function WriteMotorolaNumber(Stream: TStream; Data: Pointer;
  140. ElementSize: integer; Count: integer = 1): Integer;
  141. Each take a valid TStream descendent, a pointer to the numeric data,
  142. the element size of the data elements (either 2 or 4) and the array
  143. element count if sending an array. The default count is 1.
  144. Notes for improvement of this unit:
  145. - A version ID tag should be visible to all chunks in order to
  146. provide handling for Lightwave pre 6.0 object files.
  147. - Chunk type handlers should leave memory allocation to
  148. the base class (TLWChunk) and act more as an interface
  149. to the data pointed to by Data in TLWChunk. This would
  150. keep memory allocation very efficient and make implementing
  151. chunk handlers even easier.
  152. Author: Brian Johns [email protected]
  153. Purpose: Lightwave object support unit for Delphi.
  154. Notes: For the Lightwave Object File Format documentation please refer to
  155. http://www.lightwave3d.com/developer.
  156. Lightwave3D is a registered trademark of Newtek Incorporated.
  157. ===================================================================== *)
  158. interface
  159. uses
  160. System.Classes,
  161. System.SysUtils,
  162. System.IOUtils,
  163. System.Math,
  164. GLScene.VectorGeometry;
  165. type
  166. TID4 = array [0 .. 3] of AnsiChar;
  167. PID4 = ^TID4;
  168. TID4DynArray = array of TID4;
  169. const
  170. ID_NULL = '#0#0#0#0'; // NULL ID
  171. ID_LWSC: TID4 = 'LWSC'; // Lightwave scene file
  172. ID_FORM: TID4 = 'FORM'; // IFF Form
  173. ID_LWOB: TID4 = 'LWOB'; // Lightwave Object version 1.0 - 5.x
  174. ID_LWLO: TID4 = 'LWLO'; // Lightwave Layered Object
  175. ID_LAYR: TID4 = 'LAYR'; // LAYER
  176. ID_PNTS: TID4 = 'PNTS'; // Points chunk
  177. ID_SRFS: TID4 = 'SRFS'; // Surface Names chunk
  178. ID_POLS: TID4 = 'POLS'; // Polygons chunk
  179. ID_CRVS: TID4 = 'CRVS'; // Curves chunk
  180. ID_PCHS: TID4 = 'PCHS'; // Patches chunk
  181. ID_SURF: TID4 = 'SURF'; // Surfaces chunk
  182. ID_COLR: TID4 = 'COLR'; // Color chunk
  183. ID_FLAG: TID4 = 'FLAG'; // Surface Flags
  184. ID_LUMI: TID4 = 'LUMI'; // Luminosity
  185. ID_DIFF: TID4 = 'DIFF'; // Diffuse
  186. ID_SPEC: TID4 = 'SPEC'; // Specular
  187. ID_REFL: TID4 = 'REFL'; // Reflective
  188. ID_TRAN: TID4 = 'TRAN'; // Transparency
  189. ID_VLUM: TID4 = 'VLUM'; // Luminosity
  190. ID_VDIF: TID4 = 'VDIF'; // Diffuse
  191. ID_VSPC: TID4 = 'VSPC'; // Specularity
  192. ID_VRFL: TID4 = 'VRFL'; // Reflective
  193. ID_VTRN: TID4 = 'VTRN'; // Transparency
  194. ID_GLOS: TID4 = 'GLOS'; // Glossiness SmallInt
  195. ID_SIDE: TID4 = 'SIDE'; // Sidedness
  196. ID_RFLT: TID4 = 'RFLT'; // REFLECTION MODE (PRE 6.0)
  197. ID_RFOP: TID4 = 'RFOP'; // REFLECTION OPTIONS
  198. ID_RIMG: TID4 = 'RIMG'; // REFLECTION IMAGE
  199. ID_RSAN: TID4 = 'RSAN'; // REFLECTION MAP SEAM ANGLE
  200. ID_RIND: TID4 = 'RIND'; // REFRACTIVE INDEX
  201. ID_EDGE: TID4 = 'EDGE'; // EDGE TRANSPARENCY THRESHOLD
  202. ID_SMAN: TID4 = 'SMAN'; // SMOOTHING ANGLE RADIANS
  203. ID_ALPH: TID4 = 'ALPH'; // ALPHA MODE
  204. ID_CTEX: TID4 = 'CTEX'; // COLOR TEXTURE
  205. ID_DTEX: TID4 = 'DTEX'; // DIFFUSE TEXTURE
  206. ID_STEX: TID4 = 'STEX'; // SPECULAR TEXTURE
  207. ID_RTEX: TID4 = 'RTEX'; // REFLECTIION TEXTURE
  208. ID_TTEX: TID4 = 'TTEX'; // TRANSPARENCY TEXTURE
  209. ID_LTEX: TID4 = 'LTEX'; // LUMINANCE TEXTURE
  210. ID_BTEX: TID4 = 'BTEX'; // BUMP TEXTURE
  211. ID_TFLG: TID4 = 'TFLG'; // TEXTURE FLAGS
  212. ID_TSIZ: TID4 = 'TSIZ'; // TEXTURE SIZE
  213. ID_TCTR: TID4 = 'TCTR'; // TEXTURE CENTER
  214. ID_TFAL: TID4 = 'TFAL'; // TEXTURE FALLOFF
  215. ID_TVEL: TID4 = 'TVAL'; // TEXTURE VALUE
  216. ID_TREF: TID4 = 'TREF'; // TEXTURE REFERENCE
  217. ID_TCLR: TID4 = 'TCLR'; // TEXTURE COLOR
  218. ID_TVAL: TID4 = 'TVAL'; // TEXTURE VALUE
  219. ID_TAMP: TID4 = 'TAMP'; // TEXTURE AMPLITUDE
  220. ID_TFP0: TID4 = 'TFP0'; // TEXTURE PARAMETERS
  221. ID_TFP1: TID4 = 'TFP1'; //
  222. ID_TFP2: TID4 = 'TFP2'; //
  223. ID_TIP0: TID4 = 'TIP0'; //
  224. ID_TIP1: TID4 = 'TIP1'; //
  225. ID_TIP2: TID4 = 'TIP2'; //
  226. ID_TSP0: TID4 = 'TSP0'; //
  227. ID_TSP1: TID4 = 'TSP1'; //
  228. ID_TSP2: TID4 = 'TSP2'; //
  229. ID_TFRQ: TID4 = 'TFRQ'; //
  230. ID_TIMG: TID4 = 'TIMG'; // TEXTURE IMG
  231. ID_TALP: TID4 = 'TALP'; //
  232. ID_TWRP: TID4 = 'TWRP'; // TEXTURE WRAP
  233. ID_TAAS: TID4 = 'TAAS'; //
  234. ID_TOPC: TID4 = 'TOPC'; //
  235. ID_SHDR: TID4 = 'SHDR'; //
  236. ID_SDAT: TID4 = 'SDAT'; //
  237. ID_IMSQ: TID4 = 'IMSQ'; // IMAGE SEQUENCE
  238. ID_FLYR: TID4 = 'FLYR'; // FLYER SEQUENCE
  239. ID_IMCC: TID4 = 'IMCC'; //
  240. SURF_FLAG_LUMINOUS = 1;
  241. SURF_FLAG_OUTLINE = 2;
  242. SURF_FLAG_SMOOTHING = 4;
  243. SURF_FLAG_COLORHIGHLIGHTS = 8;
  244. SURF_FLAG_COLORFILTER = 16;
  245. SURF_FLAG_OPAQUEEDGE = 32;
  246. SURF_FLAG_TRANSPARENTEDGE = 64;
  247. SURF_FLAG_SHARPTERMINATOR = 128;
  248. SURF_FLAG_DOUBLESIDED = 256;
  249. SURF_FLAG_ADDITIVE = 512;
  250. SURF_FLAG_SHADOWALPHA = 1024;
  251. CURV_CONTINUITY_FIRST = 1;
  252. CURV_CONTINUITY_LAST = 2;
  253. IMSQ_FLAG_LOOP = 1;
  254. IMSQ_FLAG_INTERLACE = 2;
  255. ID_LWO2: TID4 = 'LWO2'; // OBJECT
  256. ID_VMAP: TID4 = 'VMAP'; // VERTEX MAP
  257. ID_TAGS: TID4 = 'TAGS'; // TAGS?
  258. ID_PTAG: TID4 = 'PTAG'; // POLYGON TAG MAP
  259. ID_VMAD: TID4 = 'VMAD'; // DISCONTINUOUS VERTEX MAP
  260. ID_ENVL: TID4 = 'ENVL'; // ENVELOPE
  261. ID_CLIP: TID4 = 'CLIP'; // CLIP
  262. ID_BBOX: TID4 = 'BBOX'; // BOUNDING BOX
  263. ID_DESC: TID4 = 'DESC'; // DESCRIPTION
  264. ID_TEXT: TID4 = 'TEXT'; // TEXT
  265. ID_ICON: TID4 = 'ICON'; // ICON
  266. ENVL_PRE: TID4 = 'PRE'#0; // PRE-BEHAVIOUR
  267. ENVL_POST: TID4 = 'POST'; // POST
  268. ENVL_KEY: TID4 = 'KEY'#0; // KEY
  269. ENVL_SPAN: TID4 = 'SPAN'; // SPAN
  270. ENVL_CHAN: TID4 = 'CHAN'; // CHAN
  271. ENVL_NAME: TID4 = 'NAME'; // NAME
  272. ID_STIL: TID4 = 'STIL'; // STILL IMAGE FILENAME
  273. ID_ISEQ: TID4 = 'ISEQ'; // IMAGE SEQUENCE
  274. ID_ANIM: TID4 = 'ANIM'; // PLUGIN ANIMATION
  275. ID_STCC: TID4 = 'STCC'; // COLOR CYCLING STILL
  276. ID_CONT: TID4 = 'CONT'; // CONTRAST
  277. ID_BRIT: TID4 = 'BRIT'; // BRIGHTNESS
  278. ID_SATR: TID4 = 'SATR'; // SATURATION
  279. ID_HUE: TID4 = 'HUE'#0; // HUE
  280. ID_GAMMA: TID4 = 'GAMM'; // GAMMA
  281. ID_NEGA: TID4 = 'NEGA'; // NEGATIVE IMAGE
  282. ID_IFLT: TID4 = 'IFLT'; // IMAGE PLUG-IN FILTER
  283. ID_PFLT: TID4 = 'PFLT'; // PIXEL PLUG-IN FILTER
  284. POLS_TYPE_FACE: TID4 = 'FACE'; // FACES
  285. POLS_TYPE_CURV: TID4 = 'CURV'; // CURVE
  286. POLS_TYPE_PTCH: TID4 = 'PTCH'; // PATCH
  287. POLS_TYPE_MBAL: TID4 = 'MBAL'; // METABALL
  288. POLS_TYPE_BONE: TID4 = 'BONE'; // SKELEGON?
  289. VMAP_TYPE_PICK: TID4 = 'PICK'; // SELECTION SET
  290. VMAP_TYPE_WGHT: TID4 = 'WGHT'; // WEIGHT MAP
  291. VMAP_TYPE_MNVW: TID4 = 'MNVW'; // SUBPATCH WEIGHT MAP
  292. VMAP_TYPE_TXUV: TID4 = 'TXUV'; // UV MAP
  293. VMAP_TYPE_RGB: TID4 = 'RGB'#0; // RGB MAP
  294. VMAP_TYPE_RGBA: TID4 = 'RGBA'; // RGBA MAP
  295. VMAP_TYPE_MORF: TID4 = 'MORF'; // MORPH MAP: RELATIVE VERTEX DISPLACEMENT
  296. VMAP_TYPE_SPOT: TID4 = 'SPOT'; // SPOT MAP: ABSOLUTE VERTEX POSITIONS
  297. PTAG_TYPE_SURF: TID4 = 'SURF'; // SURFACE
  298. PTAG_TYPE_PART: TID4 = 'PART'; // PARENT PART
  299. PTAG_TYPE_SMGP: TID4 = 'SMGP'; // SMOOTH GROUP
  300. PRE_POST_RESET = 0; // RESET
  301. PRE_POST_CONSTANT = 1; // CONSTANT
  302. PRE_POST_REPEAT = 2; // REPEAT
  303. PRE_POST_OSCILLATE = 3; // OSCILLATE
  304. PRE_POST_OFFSET = 4; // OFFSET REPEAT
  305. PRE_POST_LINEAR = 5; // LINEAR
  306. POLS_VCOUNT_MASK = $3FF;
  307. POLS_FLAGS_MASK = $FC00;
  308. SIDE_FRONT = 1;
  309. SIDE_BACK = 2;
  310. SIDE_FRONT_AND_BACK = SIDE_FRONT and SIDE_BACK;
  311. RFOP_BACKDROP = 0;
  312. RFOP_RAYTRACEANDBACKDROP = 1;
  313. RFOP_SPHERICALMAP = 2;
  314. RFOP_RAYTRACEANDSPHERICALMAP = 3;
  315. type
  316. TI1 = ShortInt;
  317. PI1 = ^TI1;
  318. TI2 = SmallInt;
  319. PI2 = ^TI2;
  320. TI4 = LongInt;
  321. PI4 = ^TI4;
  322. TU1 = Byte;
  323. PU1 = ^TU1;
  324. TU1DynArray = array of TU1;
  325. TU2 = Word;
  326. PU2 = ^TU2;
  327. TU2Array = array [0 .. 65534] of TU2;
  328. PU2Array = ^TU2Array;
  329. TU2DynArray = array of TU2;
  330. TU4 = LongWord;
  331. PU4 = ^TU4;
  332. TU4Array = array [0 .. 65534] of TU4;
  333. PU4Array = ^TU4Array;
  334. TU4DynArray = array of TU4;
  335. TF4 = Single;
  336. PF4 = ^TF4;
  337. TF4Array = array [0 .. 65534] of TF4;
  338. PF4Array = ^TF4Array;
  339. TF4DynArray = array of TF4;
  340. TANG4 = TF4;
  341. PANG4 = ^TANG4;
  342. // TS0 = PAnsiChar;
  343. TVec12 = array [0 .. 2] of TF4;
  344. PVec12 = ^TVec12;
  345. TVec12Array = array [0 .. 65534] of TVec12;
  346. PVec12Array = ^TVec12Array;
  347. TVec12DynArray = array of TVec12;
  348. TColr12 = TVec12;
  349. PColr12 = ^TColr12;
  350. TColr12DynArray = array of TColr12;
  351. TColr4 = array [0 .. 3] of TU1;
  352. PColr4 = ^TColr4;
  353. // Lightwave Chunk Struct - Used in TLWOReadCallback
  354. PLWChunkRec = ^TLWChunkRec;
  355. TLWChunkRec = record
  356. id: TID4;
  357. size: TU4;
  358. data: Pointer;
  359. end;
  360. // Lightwave SubChunk Struct - Used in TLWOReadCallback
  361. PLWSubChunkRec = ^TLWSubChunkRec;
  362. TLWSubChunkRec = record
  363. id: TID4;
  364. size: TU2;
  365. data: Pointer;
  366. end;
  367. TLWPolsInfo = record
  368. norm: TVec12;
  369. vnorms: TVec12DynArray;
  370. surfid: TU2;
  371. end;
  372. TLWPolsInfoDynArray = array of TLWPolsInfo;
  373. TLWPntsInfo = record
  374. npols: TU2;
  375. pols: TU2DynArray;
  376. end;
  377. TLWPntsInfoDynArray = array of TLWPntsInfo;
  378. TLWPolsDynArray = TU2DynArray;
  379. TLWPolyTagMapDynArray = TU2DynArray;
  380. TLWPolyTagMap = record
  381. poly: TU2;
  382. tag: TU2;
  383. end;
  384. PLWPolyTagMap = ^TLWPolyTagMap;
  385. // Value Map
  386. TLWVertexMap = record
  387. vert: TU2;
  388. values: TF4DynArray;
  389. end;
  390. TLWVertexMapDynArray = array of TLWVertexMap;
  391. TLWChunkList = class;
  392. TLWParentChunk = class;
  393. TLWChunk = class(TPersistent)
  394. private
  395. FData: Pointer;
  396. FID: TID4;
  397. FSize: TU4;
  398. FParentChunk: TLWParentChunk;
  399. FOwner: TLWChunkList;
  400. function GetRootChunks: TLWChunkList;
  401. function GetIndex: Integer;
  402. protected
  403. procedure Clear; virtual;
  404. procedure LoadData(AStream: TStream;
  405. DataStart, DataSize: LongWord); virtual;
  406. procedure Loaded; virtual;
  407. public
  408. destructor Destroy; override;
  409. class function GetID: TID4; virtual;
  410. procedure LoadFromStream(AStream: TStream); virtual;
  411. property data: Pointer read FData;
  412. property id: TID4 read FID;
  413. property size: TU4 read FSize;
  414. // ParentChunk may be nil indicating this is a root chunk. ie. TLWLayr
  415. property ParentChunk: TLWParentChunk read FParentChunk;
  416. property RootChunks: TLWChunkList read GetRootChunks;
  417. property Index: Integer read GetIndex;
  418. property Owner: TLWChunkList read FOwner;
  419. end;
  420. TLWChunkClass = class of TLWChunk;
  421. TLWSubChunk = class(TLWChunk)
  422. public
  423. procedure LoadFromStream(AStream: TStream); override;
  424. end;
  425. TLWChunkFind = procedure(AChunk: TLWChunk; Criteria: Pointer;
  426. var Found: boolean);
  427. TLWChunkList = class(TList)
  428. private
  429. FOwnsItems: boolean;
  430. FOwner: TObject;
  431. function GetItem(Index: Integer): TLWChunk;
  432. protected
  433. procedure Loaded; virtual;
  434. public
  435. constructor Create(AOwnsItems: boolean; AOwner: TObject);
  436. destructor Destroy; override;
  437. function Add(AChunk: TLWChunk): Integer;
  438. procedure Clear; override;
  439. procedure Delete(Index: Integer);
  440. function FindChunk(ChunkFind: TLWChunkFind; Criteria: Pointer;
  441. StartIndex: Integer = 0): Integer;
  442. property Items[Index: Integer]: TLWChunk read GetItem; default;
  443. property OwnsItems: boolean read FOwnsItems;
  444. property Owner: TObject read FOwner;
  445. end;
  446. TLWParentChunk = class(TLWChunk)
  447. private
  448. FItems: TLWChunkList;
  449. function GetItems: TLWChunkList;
  450. function GetFloatParam(Param: TID4): Single;
  451. function GetWordParam(Param: TID4): Word;
  452. function GetVec3Param(Param: TID4): TVec12;
  453. function GetLongParam(Param: TID4): LongWord;
  454. function GetVXParam(Param: TID4): Word;
  455. protected
  456. function GetParamAddr(Param: TID4): Pointer; virtual;
  457. procedure Clear; override;
  458. procedure Loaded; override;
  459. public
  460. property Items: TLWChunkList read GetItems;
  461. property ParamAddr[Param: TID4]: Pointer read GetParamAddr;
  462. property FloatParam[Param: TID4]: Single read GetFloatParam;
  463. property WordParam[Param: TID4]: Word read GetWordParam;
  464. property LongParam[Param: TID4]: LongWord read GetLongParam;
  465. property Vec3Param[Param: TID4]: TVec12 read GetVec3Param;
  466. property VXParam[Param: TID4]: Word read GetVXParam;
  467. end;
  468. TLWVMap = class;
  469. TLWPnts = class(TLWParentChunk)
  470. private
  471. FPnts: TVec12DynArray;
  472. FPntsInfo: TLWPntsInfoDynArray;
  473. function GetPntsCount: LongWord;
  474. function AddPoly(PntIdx, PolyIdx: Integer): Integer;
  475. protected
  476. procedure Clear; override;
  477. procedure LoadData(AStream: TStream;
  478. DataStart, DataSize: LongWord); override;
  479. public
  480. class function GetID: TID4; override;
  481. function GetVMap(VMapID: TID4; out VMap: TLWVMap): boolean;
  482. property PntsCount: LongWord read GetPntsCount;
  483. property Pnts: TVec12DynArray read FPnts;
  484. property PntsInfo: TLWPntsInfoDynArray read FPntsInfo;
  485. end;
  486. TLWPols = class(TLWParentChunk)
  487. private
  488. FPolsType: TID4;
  489. FPols: TLWPolsDynArray;
  490. FPolsInfo: TLWPolsInfoDynArray;
  491. FPolsCount: Integer;
  492. function GetPolsByIndex(AIndex: TU2): Integer;
  493. function GetIndiceCount: TU4;
  494. function GetIndice(AIndex: Integer): TU2;
  495. function GetPolsCount: Integer;
  496. procedure CalcPolsNormals;
  497. procedure CalcPntsNormals;
  498. protected
  499. procedure Clear; override;
  500. procedure LoadData(AStream: TStream;
  501. DataStart, DataSize: LongWord); override;
  502. procedure Loaded; override;
  503. public
  504. class function GetID: TID4; override;
  505. function GetPolsByPntIdx(VertIdx: TU2; var VertPolys: TU2DynArray): Integer;
  506. property PolsByIndex[AIndex: TU2]: Integer read GetPolsByIndex;
  507. property IndiceCount: TU4 read GetIndiceCount;
  508. property Indices[AIndex: Integer]: TU2 read GetIndice;
  509. property PolsType: TID4 read FPolsType;
  510. property PolsCount: Integer read GetPolsCount;
  511. property PolsInfo: TLWPolsInfoDynArray read FPolsInfo;
  512. end;
  513. TLWVMap = class(TLWChunk)
  514. private
  515. FDimensions: TU2;
  516. FName: string;
  517. FValues: TLWVertexMapDynArray;
  518. FVMapType: TID4;
  519. function GetValue(AIndex: TU2): TLWVertexMap;
  520. function GetValueCount: Integer;
  521. protected
  522. procedure Clear; override;
  523. procedure LoadData(AStream: TStream;
  524. DataStart, DataSize: LongWord); override;
  525. public
  526. class function GetID: TID4; override;
  527. property Dimensions: TU2 read FDimensions;
  528. property Name: string read FName;
  529. property Value[AIndex: TU2]: TLWVertexMap read GetValue;
  530. property ValueCount: Integer read GetValueCount;
  531. property VMapType: TID4 read FVMapType;
  532. end;
  533. TLWTags = class(TLWChunk)
  534. private
  535. FTags: TStrings;
  536. function GetTags: TStrings;
  537. protected
  538. procedure Clear; override;
  539. procedure LoadData(AStream: TStream;
  540. DataStart, DataSize: LongWord); override;
  541. public
  542. destructor Destroy; override;
  543. class function GetID: TID4; override;
  544. function TagToName(tag: TU2): string;
  545. property Tags: TStrings read GetTags;
  546. end;
  547. TLWSurf = class(TLWParentChunk)
  548. private
  549. FName: string;
  550. FSource: string;
  551. function GetSurfId: Integer;
  552. protected
  553. function GetParamAddr(Param: TID4): Pointer; override;
  554. procedure LoadData(AStream: TStream;
  555. DataStart, DataSize: LongWord); override;
  556. public
  557. destructor Destroy; override;
  558. class function GetID: TID4; override;
  559. property surfid: Integer read GetSurfId;
  560. property Name: string read FName;
  561. property Source: string read FSource;
  562. end;
  563. TLWLayr = class(TLWParentChunk)
  564. private
  565. FFlags: TU2;
  566. FName: string;
  567. FNumber: TU2;
  568. FParent: TU2;
  569. FPivot: TVec12;
  570. protected
  571. procedure LoadData(AStream: TStream;
  572. DataStart, DataSize: LongWord); override;
  573. public
  574. destructor Destroy; override;
  575. class function GetID: TID4; override;
  576. property Flags: TU2 read FFlags;
  577. property Name: string read FName;
  578. property Number: TU2 read FNumber;
  579. property Parent: TU2 read FParent;
  580. property Pivot: TVec12 read FPivot;
  581. end;
  582. TLWPTag = class(TLWChunk)
  583. private
  584. FMapType: TID4;
  585. FTagMaps: TLWPolyTagMapDynArray;
  586. FTags: TU2DynArray;
  587. function AddTag(Value: TU2): Integer;
  588. function GetTag(AIndex: Integer): TU2;
  589. function GetTagCount: Integer;
  590. function GetTagMapCount: Integer;
  591. function GetTagMaps(AIndex: Integer): TLWPolyTagMap;
  592. procedure ValidateTagInfo;
  593. protected
  594. procedure Clear; override;
  595. procedure LoadData(AStream: TStream;
  596. DataStart, DataSize: LongWord); override;
  597. public
  598. constructor Create;
  599. function GetPolsByTag(tag: TU2; var PolyIndices: TU2DynArray): Integer;
  600. class function GetID: TID4; override;
  601. property MapType: TID4 read FMapType;
  602. property TagCount: Integer read GetTagCount;
  603. property TagMapCount: Integer read GetTagMapCount;
  604. property TagMaps[AIndex: Integer]: TLWPolyTagMap read GetTagMaps; default;
  605. property Tags[AIndex: Integer]: TU2 read GetTag;
  606. end;
  607. TLWObjectFile = class(TObject)
  608. private
  609. FChunks: TLWChunkList;
  610. FFileName: string;
  611. function GetChunks: TLWChunkList;
  612. function GetCount: Integer;
  613. function GetSurfaceByName(Index: string): TLWSurf;
  614. function GetSurfaceByTag(Index: TU2): TLWSurf;
  615. public
  616. constructor Create;
  617. destructor Destroy; override;
  618. function TagToName(tag: TU2): string;
  619. procedure LoadFromFile(const AFilename: string);
  620. procedure LoadFromStream(AStream: TStream);
  621. property ChunkCount: Integer read GetCount;
  622. property Chunks: TLWChunkList read GetChunks;
  623. property FileName: string read FFileName;
  624. property SurfaceByName[Index: string]: TLWSurf read GetSurfaceByName;
  625. property SurfaceByTag[Index: TU2]: TLWSurf read GetSurfaceByTag;
  626. end;
  627. TLWClip = class(TLWParentChunk)
  628. private
  629. FClipIndex: TU4;
  630. protected
  631. procedure LoadData(AStream: TStream;
  632. DataStart, DataSize: LongWord); override;
  633. public
  634. class function GetID: TID4; override;
  635. property ClipIndex: TU4 read FClipIndex;
  636. end;
  637. TLWContentNotify = procedure(Sender: TObject; var Content: string) of object;
  638. TLWContentDir = class
  639. private
  640. FSubDirs: TStrings;
  641. FRoot: string;
  642. function GetSubDirs: TStrings;
  643. procedure SetRoot(const Value: string);
  644. procedure SetSubDirs(const Value: TStrings);
  645. // function ContentSearch(AFilename: string): string;
  646. public
  647. destructor Destroy; override;
  648. function FindContent(AFilename: string): string;
  649. property Root: string read FRoot write SetRoot;
  650. property SubDirs: TStrings read GetSubDirs write SetSubDirs;
  651. end;
  652. TLWOReadCallback = procedure(Chunk: TLWChunkRec; data: Pointer); cdecl;
  653. procedure RegisterChunkClass(ChunkClass: TLWChunkClass);
  654. function LoadLW0FromStream(Stream: TStream; ReadCallback: TLWOReadCallback;
  655. UserData: Pointer): LongWord; cdecl;
  656. function LoadLWOFromFile(const AFilename: string;
  657. ReadCallback: TLWOReadCallback; UserData: Pointer): LongWord;
  658. procedure ReadMotorolaNumber(Stream: TStream; data: Pointer;
  659. ElementSize: Integer; Count: Integer = 1);
  660. function WriteMotorolaNumber(Stream: TStream; data: Pointer;
  661. ElementSize: Integer; Count: Integer = 1): Integer;
  662. function ReadS0(Stream: TStream; out Str: string): Integer;
  663. procedure WriteS0(Stream: TStream; data: string);
  664. procedure WriteU4AsVX(Stream: TStream; data: Pointer; Count: Integer);
  665. function ReadVXAsU4(Stream: TStream; data: Pointer; Count: Integer = 1)
  666. : Integer;
  667. procedure ReverseByteOrder(ValueIn: Pointer; size: Integer; Count: Integer = 1);
  668. function ToDosPath(const Path: string): string;
  669. function ToUnixPath(const Path: string): string;
  670. function ID4ToInt(const id: TID4): Integer;
  671. // ChunkFind procedures
  672. procedure FindChunkById(AChunk: TLWChunk; data: Pointer; var Found: boolean);
  673. procedure FindSurfaceByName(AChunk: TLWChunk; AName: Pointer;
  674. var Found: boolean);
  675. procedure FindSurfaceByTag(AChunk: TLWChunk; ATag: Pointer; var Found: boolean);
  676. procedure FindVMapByName(AChunk: TLWChunk; AName: Pointer; var Found: boolean);
  677. procedure FindClipByClipIndex(AChunk: TLWChunk; AIndex: Pointer;
  678. var Found: boolean);
  679. function GetContentDir: TLWContentDir;
  680. // --------------------------------------------------------------------
  681. implementation
  682. // --------------------------------------------------------------------
  683. type
  684. PWord = ^Word;
  685. PLongWord = ^LongWord;
  686. var
  687. ChunkClasses: TList;
  688. ContentDir: TLWContentDir;
  689. function ToDosPath(const Path: string): string;
  690. var
  691. i: Integer;
  692. begin
  693. result := Path;
  694. for i := 1 to Length(result) do
  695. if result[i] = '/' then
  696. result[i] := '\';
  697. end;
  698. function ToUnixPath(const Path: string): string;
  699. var
  700. i: Integer;
  701. begin
  702. result := Path;
  703. for i := 1 to Length(result) do
  704. if result[i] = '\' then
  705. result[i] := '/';
  706. end;
  707. function GetContentDir: TLWContentDir;
  708. begin
  709. if ContentDir = nil then
  710. ContentDir := TLWContentDir.Create;
  711. result := ContentDir;
  712. end;
  713. procedure FindChunkById(AChunk: TLWChunk; data: Pointer; var Found: boolean);
  714. begin
  715. if AChunk.FID = PID4(data)^ then
  716. Found := true
  717. else
  718. Found := false;
  719. end;
  720. procedure FindClipByClipIndex(AChunk: TLWChunk; AIndex: Pointer;
  721. var Found: boolean);
  722. begin
  723. if (AChunk is TLWClip) and (TLWClip(AChunk).ClipIndex = PU2(AIndex)^) then
  724. Found := true;
  725. end;
  726. procedure FindSurfaceByName(AChunk: TLWChunk; AName: Pointer;
  727. var Found: boolean);
  728. begin
  729. if (AChunk is TLWSurf) and (TLWSurf(AChunk).Name = PString(AName)^) then
  730. Found := true;
  731. end;
  732. procedure FindSurfaceByTag(AChunk: TLWChunk; ATag: Pointer; var Found: boolean);
  733. begin
  734. if (AChunk is TLWSurf) and (TLWSurf(AChunk).surfid = PU2(ATag)^) then
  735. Found := true;
  736. end;
  737. procedure FindVMapByName(AChunk: TLWChunk; AName: Pointer; var Found: boolean);
  738. begin
  739. if (AChunk is TLWVMap) and (TLWVMap(AChunk).Name = PString(AName)^) then
  740. Found := true;
  741. end;
  742. function VecAdd(v1, v2: TVec12): TVec12;
  743. begin
  744. result[0] := v1[0] + v2[0];
  745. result[1] := v1[1] + v2[1];
  746. result[2] := v1[2] + v2[2];
  747. end;
  748. function VecSub(v1, v2: TVec12): TVec12;
  749. begin
  750. result[0] := v1[0] - v2[0];
  751. result[1] := v1[1] - v2[1];
  752. result[2] := v1[2] - v2[2];
  753. end;
  754. function VecCross(v1, v2: TVec12): TVec12;
  755. begin
  756. result[0] := v1[1] * v2[2] - v1[2] * v2[1];
  757. result[1] := v1[2] * v2[0] - v1[0] * v2[2];
  758. result[2] := v1[0] * v2[1] - v1[1] * v2[0];
  759. end;
  760. function VecDot(v1, v2: TVec12): TF4;
  761. begin
  762. result := v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
  763. end;
  764. function VecNorm(v: TVec12): TVec12;
  765. var
  766. mag: TF4;
  767. begin
  768. mag := Sqrt(VecDot(v, v));
  769. if mag > 0 then
  770. mag := 1 / mag;
  771. result[0] := v[0] * mag;
  772. result[1] := v[1] * mag;
  773. result[2] := v[2] * mag;
  774. end;
  775. function CalcPlaneNormal(v1, v2, v3: TVec12): TVec12;
  776. var
  777. e1, e2: TVec12;
  778. begin
  779. e1 := VecSub(v2, v1);
  780. e2 := VecSub(v3, v1);
  781. result := VecCross(e1, e2);
  782. result := VecNorm(result);
  783. end;
  784. procedure FindSurfByName(Chunk: TLWChunk; var Found: boolean);
  785. begin
  786. end;
  787. (*-----------------------------------------------------------------------------
  788. Procedure: GetChunkClasses
  789. Date: 08-Aug-2002
  790. Arguments: None
  791. Result: TClassList
  792. Singleton access for the chunk class list.
  793. -----------------------------------------------------------------------------*)
  794. function GetChunkClasses: TList;
  795. begin
  796. if ChunkClasses = nil then
  797. ChunkClasses := TList.Create;
  798. result := ChunkClasses;
  799. end;
  800. procedure UnRegisterChunkClasses;
  801. var
  802. i: Integer;
  803. begin
  804. with GetChunkClasses do
  805. for i := 0 to Count - 1 do
  806. UnregisterClass(TPersistentClass(Items[i]));
  807. end;
  808. (*-----------------------------------------------------------------------------
  809. Procedure: RegisterChunkClass
  810. Date: 08-Aug-2002
  811. Arguments: ChunkClass: TLWChunkClass
  812. Result: None
  813. Adds a user defined chunk class to the chunk class list.
  814. -----------------------------------------------------------------------------*)
  815. procedure RegisterChunkClass(ChunkClass: TLWChunkClass);
  816. begin
  817. GetChunkClasses.Add(ChunkClass);
  818. // if FindClass(ChunkClass.ClassName) <> nil then
  819. // UnRegisterClass(ChunkClass);
  820. // RegisterClass(ChunkClass);
  821. end;
  822. (*-----------------------------------------------------------------------------
  823. Procedure: GetChunkClass
  824. Date: 08-Aug-2002
  825. Arguments: ChunkID: TID4
  826. Result: TLWChunkClass
  827. Returns the chunk class associated with ChunkID.
  828. -----------------------------------------------------------------------------*)
  829. function GetChunkClass(ChunkID: TID4; ADefault: TLWChunkClass): TLWChunkClass;
  830. var
  831. i: Integer;
  832. begin
  833. if ADefault = nil then
  834. result := TLWChunk
  835. else
  836. result := ADefault;
  837. for i := 0 to ChunkClasses.Count - 1 do
  838. begin
  839. if TLWChunkClass(ChunkClasses.Items[i]).GetID = ChunkID then
  840. begin
  841. result := TLWChunkClass(ChunkClasses.Items[i]);
  842. Exit;
  843. end;
  844. end;
  845. end;
  846. (*-----------------------------------------------------------------------------
  847. Procedure: Tokenize
  848. Date: 08-Aug-2002
  849. Arguments: const Src: string; Delimiter: Char; Dst: TStrings
  850. Result: None
  851. Breaks up a string into TStrings items when the Delimiter character is
  852. encountered.
  853. -----------------------------------------------------------------------------*)
  854. procedure Tokenize(const Src: string; Delimiter: Char; Dst: TStrings);
  855. var
  856. i, L, SL: Integer;
  857. SubStr: string;
  858. begin
  859. if Dst = nil then
  860. Exit;
  861. L := Length(Src);
  862. if (L = 0) or (Dst = nil) then
  863. Exit;
  864. SubStr := '';
  865. for i := 1 to L do
  866. begin
  867. if (Src[i] <> Delimiter) then
  868. SubStr := SubStr + Src[i]
  869. else
  870. begin
  871. SL := Length(SubStr);
  872. if SL > 0 then
  873. begin
  874. Dst.Add(SubStr);
  875. SubStr := '';
  876. end;
  877. end;
  878. end;
  879. if Length(SubStr) > 0 then
  880. Dst.Add(SubStr);
  881. end;
  882. (*-----------------------------------------------------------------------------
  883. Procedure: LoadLW0FromStream
  884. Date: 08-Aug-2002
  885. Arguments: Stream: TStream; ReadCallback: TLWOReadCallback; UserData: Pointer
  886. Result: LongWord
  887. -----------------------------------------------------------------------------*)
  888. function LoadLW0FromStream(Stream: TStream; ReadCallback: TLWOReadCallback;
  889. UserData: Pointer): LongWord;
  890. var
  891. Chunk: TLWChunkRec;
  892. CurId: TID4;
  893. StartPos, CurSize: TU4;
  894. begin
  895. try
  896. Stream.Read(CurId, 4);
  897. ReadMotorolaNumber(Stream, @CurSize, 4);
  898. if UpperCase(string(CurId)) = 'FORM' then
  899. begin
  900. Stream.Read(CurId, 4);
  901. end
  902. else
  903. raise Exception.Create
  904. ('Invalid magic number. Not a valid Lightwave Object');
  905. with Stream do
  906. while Position < size do
  907. begin
  908. Read(Chunk, 8);
  909. ReverseByteOrder(@Chunk.size, 4);
  910. StartPos := Position;
  911. GetMem(Chunk.data, Chunk.size);
  912. Stream.Read(Chunk.data^, Chunk.size);
  913. if Assigned(ReadCallback) then
  914. ReadCallback(Chunk, UserData);
  915. FreeMem(Chunk.data, Chunk.size);
  916. Position := StartPos + Chunk.size + (StartPos + Chunk.size) mod 2;
  917. end;
  918. Stream.Free;
  919. result := High(LongWord);
  920. except
  921. On E: Exception do
  922. begin
  923. Stream.Free;
  924. result := 0;
  925. end;
  926. end;
  927. end;
  928. function LoadLWOFromFile(const AFilename: String;
  929. ReadCallback: TLWOReadCallback; UserData: Pointer): LongWord;
  930. var
  931. Stream: TStream;
  932. begin
  933. Stream := TFileStream.Create(AFilename, fmOpenRead);
  934. try
  935. result := LoadLW0FromStream(Stream, ReadCallback, UserData);
  936. finally
  937. Stream.Free;
  938. end;
  939. end;
  940. procedure ReverseByteOrder(ValueIn: Pointer; size: Integer; Count: Integer = 1);
  941. var
  942. W: Word;
  943. pB: PByte;
  944. Blo, Bhi: Byte;
  945. L: LongWord;
  946. i: Integer;
  947. begin
  948. i := 0;
  949. case size of
  950. 2:
  951. begin
  952. while i < Count do
  953. begin
  954. W := PU2Array(ValueIn)^[i];
  955. pB := @W;
  956. Blo := pB^;
  957. Inc(pB);
  958. Bhi := pB^;
  959. pB^ := Blo;
  960. Dec(pB);
  961. pB^ := Bhi;
  962. PU2Array(ValueIn)^[i] := W;
  963. Inc(i);
  964. end;
  965. end;
  966. 4:
  967. begin
  968. while i < Count do
  969. begin
  970. L := PU4Array(ValueIn)^[i];
  971. pB := @W;
  972. Blo := pB^;
  973. Inc(pB);
  974. Bhi := pB^;
  975. pB^ := Blo;
  976. Dec(pB);
  977. pB^ := Bhi;
  978. PU4Array(ValueIn)^[i] := L;
  979. Inc(i);
  980. end;
  981. end;
  982. else
  983. raise Exception.Create('Lightwave.ReverseByteOrder: Invalid Size = ' +
  984. IntToStr(size));
  985. end;
  986. end;
  987. procedure ReadMotorolaNumber(Stream: TStream; data: Pointer;
  988. ElementSize: Integer; Count: Integer = 1);
  989. begin
  990. Stream.Read(data^, Count * ElementSize);
  991. if (ElementSize = 2) or (ElementSize = 4) then
  992. ReverseByteOrder(data, ElementSize, Count);
  993. end;
  994. function WriteMotorolaNumber(Stream: TStream; data: Pointer;
  995. ElementSize: Integer; Count: Integer = 1): Integer;
  996. var
  997. TempData: Pointer;
  998. begin
  999. result := 0;
  1000. if data <> nil then
  1001. begin
  1002. TempData := AllocMem(ElementSize * Count);
  1003. try
  1004. if (ElementSize = 2) or (ElementSize = 4) then
  1005. ReverseByteOrder(TempData, ElementSize, Count);
  1006. result := Stream.Write(data, Count * ElementSize);
  1007. except
  1008. on E: Exception do
  1009. begin
  1010. FreeMem(TempData, Count * ElementSize);
  1011. raise;
  1012. end;
  1013. end;
  1014. end;
  1015. end;
  1016. function ReadS0(Stream: TStream; out Str: string): Integer;
  1017. var
  1018. Buf: array [0 .. 1] of AnsiChar;
  1019. StrBuf: string;
  1020. begin
  1021. Stream.Read(Buf, 2);
  1022. StrBuf := '';
  1023. while Buf[1] <> #0 do
  1024. begin
  1025. StrBuf := StrBuf + string(Buf);
  1026. Stream.Read(Buf, 2);
  1027. end;
  1028. if Buf[0] <> #0 then
  1029. StrBuf := StrBuf + Char(Buf[0]);
  1030. Str := Copy(StrBuf, 1, Length(StrBuf));
  1031. result := Length(Str) + 1;
  1032. result := result + (result mod 2);
  1033. end;
  1034. function ValueOfVX(VX: Pointer): TU4;
  1035. var
  1036. TmpU2: TU2;
  1037. TmpU4: TU4;
  1038. begin
  1039. if PU1(VX)^ = $FF then
  1040. begin
  1041. TmpU4 := TU4(PU1(VX)^) and $FFFFFFF0;
  1042. ReverseByteOrder(@TmpU4, 4);
  1043. end
  1044. else
  1045. begin
  1046. TmpU2 := TU2(PU2(VX)^);
  1047. ReverseByteOrder(@TmpU2, 2);
  1048. TmpU4 := TmpU2;
  1049. end;
  1050. result := TmpU4;
  1051. end;
  1052. function ReadVXAsU4(Stream: TStream; data: Pointer; Count: Integer = 1)
  1053. : Integer;
  1054. var
  1055. i, ReadCount: Integer;
  1056. BufByte: Byte;
  1057. TempU2: TU2;
  1058. begin
  1059. ReadCount := 0;
  1060. for i := 0 to Count - 1 do
  1061. begin
  1062. Stream.Read(BufByte, 1);
  1063. Stream.Position := Stream.Position - 1;
  1064. if BufByte = 255 then
  1065. begin
  1066. Stream.Read(data^, SizeOf(TU4));
  1067. PU4Array(data)^[i] := PU4Array(data)^[i] and $FFFFFFF0;
  1068. ReverseByteOrder(data, SizeOf(TU4));
  1069. Inc(ReadCount, 4);
  1070. end
  1071. else
  1072. begin
  1073. Stream.Read(TempU2, SizeOf(TU2));
  1074. ReverseByteOrder(@TempU2, SizeOf(TU2));
  1075. PU4Array(data)^[i] := TempU2;
  1076. Inc(ReadCount, 2);
  1077. end;
  1078. end;
  1079. result := ReadCount;
  1080. end;
  1081. function ReadVXAsU2(Stream: TStream; data: Pointer; Count: Integer = 1)
  1082. : Integer;
  1083. var
  1084. i, ReadCount: Integer;
  1085. BufByte: Byte;
  1086. TempU2: TU2;
  1087. begin
  1088. ReadCount := 0;
  1089. for i := 0 to Count - 1 do
  1090. begin
  1091. Stream.Read(BufByte, 1);
  1092. Stream.Position := Stream.Position - 1;
  1093. if BufByte = 255 then
  1094. begin
  1095. Stream.Position := Stream.Position + 4;
  1096. PU2Array(data)^[i] := 0;
  1097. Inc(ReadCount, 4);
  1098. end
  1099. else
  1100. begin
  1101. Stream.Read(TempU2, SizeOf(TU2));
  1102. ReverseByteOrder(@TempU2, SizeOf(TU2));
  1103. PU2Array(data)^[i] := TempU2;
  1104. Inc(ReadCount, 2);
  1105. end;
  1106. end;
  1107. result := ReadCount;
  1108. end;
  1109. procedure WriteS0(Stream: TStream; data: string);
  1110. begin
  1111. // ToDo: WriteS0
  1112. end;
  1113. procedure WriteU4AsVX(Stream: TStream; data: Pointer; Count: Integer);
  1114. var
  1115. i: Integer;
  1116. TempU2: TU2;
  1117. begin
  1118. for i := 0 to Count - 1 do
  1119. begin
  1120. if PU4Array(data)^[i] < 65280 then
  1121. begin
  1122. TempU2 := PU4Array(data)^[i];
  1123. WriteMotorolaNumber(Stream, @TempU2, SizeOf(TU2));
  1124. end
  1125. else
  1126. WriteMotorolaNumber(Stream, data, SizeOf(TU4));
  1127. end;
  1128. end;
  1129. type
  1130. PInteger = ^Integer;
  1131. function ID4ToInt(const id: TID4): Integer;
  1132. var
  1133. TmpId: AnsiString;
  1134. begin
  1135. TmpId := id;
  1136. TmpId := AnsiString(UpperCase(string(id)));
  1137. result := PInteger(@TmpId)^;
  1138. end;
  1139. (*********************************** TLWChunk ********************************)
  1140. destructor TLWChunk.Destroy;
  1141. begin
  1142. Clear;
  1143. inherited;
  1144. end;
  1145. procedure TLWChunk.Clear;
  1146. begin
  1147. FreeMem(FData, FSize);
  1148. FSize := 0;
  1149. FData := nil;
  1150. end;
  1151. class function TLWChunk.GetID: TID4;
  1152. begin
  1153. result := #0#0#0#0;
  1154. end;
  1155. procedure TLWChunk.LoadData(AStream: TStream; DataStart, DataSize: LongWord);
  1156. begin
  1157. GetMem(FData, DataSize);
  1158. AStream.Read(PByteArray(FData)^[0], DataSize);
  1159. end;
  1160. procedure TLWChunk.LoadFromStream(AStream: TStream);
  1161. var
  1162. DataStart: Integer;
  1163. DataSize: TU4;
  1164. begin
  1165. with AStream do
  1166. begin
  1167. ReadMotorolaNumber(AStream, @DataSize, 4);
  1168. DataStart := Position;
  1169. FSize := DataSize;
  1170. LoadData(AStream, DataStart, DataSize);
  1171. Position := Cardinal(DataStart) + DataSize +
  1172. (Cardinal(DataStart) + DataSize) mod 2;
  1173. end;
  1174. end;
  1175. (********************************* TLWChunkList *******************************)
  1176. constructor TLWChunkList.Create(AOwnsItems: boolean; AOwner: TObject);
  1177. begin
  1178. inherited Create;
  1179. FOwnsItems := AOwnsItems;
  1180. FOwner := AOwner;
  1181. end;
  1182. destructor TLWChunkList.Destroy;
  1183. begin
  1184. Clear;
  1185. inherited;
  1186. end;
  1187. procedure TLWChunkList.Clear;
  1188. begin
  1189. while Count > 0 do
  1190. Delete(Count - 1);
  1191. inherited;
  1192. end;
  1193. procedure TLWChunkList.Delete(Index: Integer);
  1194. begin
  1195. if FOwnsItems then
  1196. Items[Index].Free;
  1197. inherited Delete(Index);
  1198. end;
  1199. function TLWChunkList.GetItem(Index: Integer): TLWChunk;
  1200. begin
  1201. result := TLWChunk(inherited Items[Index]);
  1202. end;
  1203. (******************************** TLWObjectFile *******************************)
  1204. constructor TLWObjectFile.Create;
  1205. begin
  1206. inherited;
  1207. end;
  1208. destructor TLWObjectFile.Destroy;
  1209. begin
  1210. FreeAndNil(FChunks);
  1211. inherited;
  1212. end;
  1213. function TLWObjectFile.GetChunks: TLWChunkList;
  1214. begin
  1215. if FChunks = nil then
  1216. FChunks := TLWChunkList.Create(true, Self);
  1217. result := FChunks;
  1218. end;
  1219. function TLWObjectFile.GetCount: Integer;
  1220. begin
  1221. result := Chunks.Count;
  1222. end;
  1223. function TLWObjectFile.GetSurfaceByName(Index: string): TLWSurf;
  1224. var
  1225. SurfIdx: Integer;
  1226. begin
  1227. SurfIdx := Chunks.FindChunk(@FindSurfaceByName, @Index, 0);
  1228. if SurfIdx <> -1 then
  1229. result := TLWSurf(Chunks[SurfIdx])
  1230. else
  1231. result := nil;
  1232. end;
  1233. function TLWObjectFile.GetSurfaceByTag(Index: TU2): TLWSurf;
  1234. var
  1235. TagName: string;
  1236. begin
  1237. TagName := TagToName(Index);
  1238. result := SurfaceByName[TagName];
  1239. end;
  1240. procedure TLWObjectFile.LoadFromFile(const AFilename: string);
  1241. var
  1242. Stream: TMemoryStream;
  1243. begin
  1244. Stream := TMemoryStream.Create;
  1245. try
  1246. Stream.LoadFromFile(AFilename);
  1247. LoadFromStream(Stream);
  1248. Stream.Free;
  1249. FFileName := AFilename;
  1250. except
  1251. on E: Exception do
  1252. begin
  1253. Stream.Free;
  1254. raise;
  1255. end;
  1256. end;
  1257. end;
  1258. procedure TLWObjectFile.LoadFromStream(AStream: TStream);
  1259. var
  1260. CurId: TID4;
  1261. CurSize: LongWord;
  1262. CurPnts, CurPols, CurItems: TLWChunkList;
  1263. begin
  1264. CurPols := nil;
  1265. CurPnts := nil;
  1266. AStream.Read(CurId, 4);
  1267. ReadMotorolaNumber(AStream, @CurSize, 4);
  1268. if UpperCase(string(CurId)) = 'FORM' then
  1269. begin
  1270. AStream.Read(CurId, 4);
  1271. if CurId <> 'LWO2' then
  1272. raise Exception.Create
  1273. ('Only Version 6.0+ version objects are supported.');
  1274. end
  1275. else
  1276. raise Exception.Create
  1277. ('Invalid magic number. Not a valid Lightwave Object');
  1278. CurItems := Chunks;
  1279. while AStream.Position < AStream.size do
  1280. begin
  1281. AStream.Read(CurId, 4);
  1282. if (CurId = ID_PTAG) then
  1283. begin
  1284. CurPols.Add(GetChunkClass(CurId, TLWChunk).Create);
  1285. {$IFDEF WIN32}
  1286. CurPols[CurPols.Count - 1].FID := CurId;
  1287. {$ELSE}
  1288. // CurPols[CurPols.Count - 1].FID := CurId;
  1289. {$ENDIF}
  1290. LoadFromStream(AStream);
  1291. end
  1292. else if (CurId = ID_VMAP) or (CurId = ID_VMAD) then
  1293. begin
  1294. CurPnts.Add(GetChunkClass(CurId, TLWChunk).Create);
  1295. {$IFDEF WIN32}
  1296. CurPnts[CurPnts.Count - 1].FID := CurId;
  1297. {$ELSE}
  1298. // CurPnts[CurPnts.Count - 1].FID := CurId;
  1299. {$ENDIF}
  1300. LoadFromStream(AStream);
  1301. end
  1302. else
  1303. begin
  1304. if (CurId = ID_LAYR) or (CurId = ID_SURF) or (CurId = ID_TAGS) or
  1305. (CurId = ID_CLIP) then
  1306. CurItems := Chunks;
  1307. CurItems.Add(GetChunkClass(CurId, TLWChunk).Create);
  1308. {$IFDEF WIN32}
  1309. CurItems[CurItems.Count - 1].FID := CurId;
  1310. {$ELSE}
  1311. // CurItems[CurItems.Count - 1].FID := CurId;
  1312. {$ENDIF}
  1313. LoadFromStream(AStream);
  1314. end;
  1315. if CurId = ID_LAYR then
  1316. CurItems := TLWParentChunk(CurItems[CurItems.Count - 1]).Items
  1317. else if CurId = ID_POLS then
  1318. CurPols := TLWParentChunk(CurItems[CurItems.Count - 1]).Items
  1319. else if CurId = ID_PNTS then
  1320. CurPnts := TLWParentChunk(CurItems[CurItems.Count - 1]).Items;
  1321. end;
  1322. Chunks.Loaded;
  1323. end;
  1324. (*********************************** TLWPnts **********************************)
  1325. function TLWPnts.AddPoly(PntIdx, PolyIdx: Integer): Integer;
  1326. var
  1327. i, L: Integer;
  1328. begin
  1329. // DONE: Pnts.AddPoly
  1330. for i := 0 to FPntsInfo[PntIdx].npols - 1 do
  1331. begin
  1332. if FPntsInfo[PntIdx].pols[i] = PolyIdx then
  1333. begin
  1334. result := i;
  1335. Exit;
  1336. end;
  1337. end;
  1338. L := Length(FPntsInfo[PntIdx].pols);
  1339. SetLength(FPntsInfo[PntIdx].pols, L + 1);
  1340. FPntsInfo[PntIdx].npols := L + 1;
  1341. FPntsInfo[PntIdx].pols[L] := PolyIdx;
  1342. result := L;
  1343. end;
  1344. procedure TLWPnts.Clear;
  1345. var
  1346. i: Integer;
  1347. begin
  1348. for i := 0 to PntsCount - 1 do
  1349. SetLength(FPntsInfo[i].pols, 0);
  1350. SetLength(FPntsInfo, 0);
  1351. SetLength(FPnts, 0);
  1352. end;
  1353. function TLWPnts.GetPntsCount: LongWord;
  1354. begin
  1355. result := Length(FPnts);
  1356. end;
  1357. class function TLWPnts.GetID: TID4;
  1358. begin
  1359. result := ID_PNTS;
  1360. end;
  1361. function TLWPnts.GetVMap(VMapID: TID4; out VMap: TLWVMap): boolean;
  1362. var
  1363. i: Integer;
  1364. begin
  1365. result := false;
  1366. for i := 0 to Items.Count - 1 do
  1367. begin
  1368. if (Items[i] is TLWVMap) and (TLWVMap(Items[i]).VMapType = VMapID) then
  1369. begin
  1370. result := true;
  1371. VMap := TLWVMap(Items[i]);
  1372. Exit;
  1373. end;
  1374. end;
  1375. end;
  1376. procedure TLWPnts.LoadData(AStream: TStream; DataStart, DataSize: LongWord);
  1377. begin
  1378. SetLength(FPnts, DataSize div 12);
  1379. // allocate storage for DataSize div 12 points
  1380. SetLength(FPntsInfo, DataSize div 12); // Point info
  1381. ReadMotorolaNumber(AStream, @FPnts[0], 4, DataSize div 4);
  1382. // read the point data
  1383. end;
  1384. (*********************************** TLWPols **********************************)
  1385. procedure TLWPols.CalcPolsNormals;
  1386. var
  1387. i, j, PolyIdx: Integer;
  1388. Pnts: TLWPnts;
  1389. begin
  1390. if IndiceCount = 0 then
  1391. Exit;
  1392. with ParentChunk as TLWLayr do
  1393. Pnts := TLWPnts(Items[Items.FindChunk(@FindChunkById, @ID_PNTS, 0)]);
  1394. for PolyIdx := 0 to FPolsCount - 1 do
  1395. begin
  1396. // DONE: call Pnts.AddPoly
  1397. i := PolsByIndex[PolyIdx];
  1398. with Pnts do
  1399. begin
  1400. for j := 1 to Indices[i] do
  1401. AddPoly(Indices[i + j], PolyIdx);
  1402. SetLength(FPolsInfo[PolyIdx].vnorms, Indices[i]);
  1403. if Indices[PolyIdx] > 2 then
  1404. FPolsInfo[PolyIdx].norm := CalcPlaneNormal(Pnts[Indices[i + 1]],
  1405. Pnts[Indices[i + 2]], Pnts[Indices[i + 3]])
  1406. else
  1407. FPolsInfo[PolyIdx].norm := VecNorm(Pnts[Indices[i + 1]]);
  1408. end;
  1409. end;
  1410. end;
  1411. procedure TLWPols.Clear;
  1412. var
  1413. i: Integer;
  1414. begin
  1415. for i := 0 to FPolsCount - 1 do
  1416. SetLength(FPolsInfo[i].vnorms, 0);
  1417. SetLength(FPolsInfo, 0);
  1418. SetLength(FPols, 0);
  1419. end;
  1420. function TLWPols.GetPolsByIndex(AIndex: TU2): Integer;
  1421. var
  1422. i, cnt: Cardinal;
  1423. begin
  1424. result := -1;
  1425. i := 0;
  1426. cnt := 0;
  1427. if AIndex = 0 then
  1428. begin
  1429. result := 0;
  1430. Exit;
  1431. end;
  1432. while (i < IndiceCount - 1) and (cnt <> AIndex) do
  1433. begin
  1434. Inc(i, Indices[i] + 1);
  1435. Inc(cnt);
  1436. end;
  1437. if cnt = AIndex then
  1438. result := i;
  1439. end;
  1440. class function TLWPols.GetID: TID4;
  1441. begin
  1442. result := ID_POLS;
  1443. end;
  1444. function TLWPols.GetIndiceCount: TU4;
  1445. begin
  1446. result := Length(FPols);
  1447. end;
  1448. function TLWPols.GetIndice(AIndex: Integer): TU2;
  1449. begin
  1450. result := FPols[AIndex];
  1451. end;
  1452. function TLWPols.GetPolsCount: Integer;
  1453. begin
  1454. result := FPolsCount;
  1455. end;
  1456. procedure TLWPols.LoadData(AStream: TStream; DataStart, DataSize: LongWord);
  1457. var
  1458. EndPos: Integer;
  1459. Idx: TU4;
  1460. TmpU2: TU2;
  1461. begin
  1462. Idx := 0;
  1463. EndPos := DataStart + DataSize;
  1464. with AStream do
  1465. begin
  1466. Read(FPolsType, 4);
  1467. // To avoid memory manager hits, set an estimate length of indices
  1468. SetLength(FPols, (DataSize - 4) div 2);
  1469. while Position < EndPos do
  1470. begin
  1471. ReadMotorolaNumber(AStream, @FPols[Idx], 2);
  1472. TmpU2 := FPols[Idx] and POLS_VCOUNT_MASK;
  1473. ReadVXAsU2(AStream, @FPols[Idx + 1], TmpU2);
  1474. Inc(Idx, FPols[Idx] + 1);
  1475. Inc(FPolsCount);
  1476. end;
  1477. // correct length estimate errors if any
  1478. if (Idx + 1) < Cardinal(Length(FPols)) then
  1479. SetLength(FPols, Idx + 1);
  1480. end;
  1481. SetLength(FPolsInfo, FPolsCount);
  1482. CalcPolsNormals;
  1483. end;
  1484. (*********************************** TLWVMap **********************************)
  1485. procedure TLWVMap.Clear;
  1486. var
  1487. i: Integer;
  1488. begin
  1489. for i := 0 to Length(FValues) - 1 do
  1490. SetLength(FValues[i].values, 0);
  1491. SetLength(FValues, 0);
  1492. end;
  1493. class function TLWVMap.GetID: TID4;
  1494. begin
  1495. result := ID_VMAP;
  1496. end;
  1497. function TLWVMap.GetValue(AIndex: TU2): TLWVertexMap;
  1498. begin
  1499. result := FValues[AIndex];
  1500. end;
  1501. function TLWVMap.GetValueCount: Integer;
  1502. begin
  1503. result := Length(FValues);
  1504. end;
  1505. procedure TLWVMap.LoadData(AStream: TStream; DataStart, DataSize: LongWord);
  1506. var
  1507. Idx: TU4;
  1508. begin
  1509. Idx := 0;
  1510. with AStream do
  1511. begin
  1512. Read(FVMapType, 4);
  1513. ReadMotorolaNumber(AStream, @FDimensions, 2);
  1514. ReadS0(AStream, FName);
  1515. if FDimensions > 0 then
  1516. begin
  1517. while Cardinal(Position) < (DataStart + DataSize) do
  1518. begin
  1519. SetLength(FValues, Length(FValues) + 1);
  1520. ReadVXAsU2(AStream, @FValues[Idx].vert, 1);
  1521. SetLength(FValues[Idx].values, Dimensions * 4);
  1522. ReadMotorolaNumber(AStream, @FValues[Idx].values[0], 4, Dimensions);
  1523. Inc(Idx);
  1524. end;
  1525. end;
  1526. end;
  1527. end;
  1528. (*********************************** TLWTags **********************************)
  1529. destructor TLWTags.Destroy;
  1530. begin
  1531. inherited;
  1532. end;
  1533. procedure TLWTags.Clear;
  1534. begin
  1535. FreeAndNil(FTags);
  1536. end;
  1537. class function TLWTags.GetID: TID4;
  1538. begin
  1539. result := ID_TAGS;
  1540. end;
  1541. function TLWTags.GetTags: TStrings;
  1542. begin
  1543. if FTags = nil then
  1544. FTags := TStringList.Create;
  1545. result := FTags;
  1546. end;
  1547. procedure TLWTags.LoadData(AStream: TStream; DataStart, DataSize: LongWord);
  1548. var
  1549. EndPos: TU4;
  1550. TmpStr: string;
  1551. begin
  1552. EndPos := DataStart + DataSize;
  1553. while Cardinal(AStream.Position) < Cardinal(EndPos) do
  1554. begin
  1555. ReadS0(AStream, TmpStr);
  1556. Tags.Add(TmpStr);
  1557. TmpStr := '';
  1558. end;
  1559. end;
  1560. function TLWTags.TagToName(tag: TU2): string;
  1561. begin
  1562. result := Tags[tag];
  1563. end;
  1564. (********************************* TLWSubChunk ********************************)
  1565. procedure TLWSubChunk.LoadFromStream(AStream: TStream);
  1566. var
  1567. DataStart: Integer;
  1568. DataSize: TU2;
  1569. begin
  1570. with AStream do
  1571. begin
  1572. ReadMotorolaNumber(AStream, @DataSize, 2);
  1573. DataStart := Position;
  1574. FSize := DataSize;
  1575. LoadData(AStream, DataStart, DataSize);
  1576. Position := DataStart + DataSize + (DataStart + DataSize) mod 2;
  1577. end;
  1578. end;
  1579. (*********************************** TLWLayr **********************************)
  1580. destructor TLWLayr.Destroy;
  1581. begin
  1582. inherited;
  1583. end;
  1584. class function TLWLayr.GetID: TID4;
  1585. begin
  1586. result := ID_LAYR;
  1587. end;
  1588. procedure TLWLayr.LoadData(AStream: TStream; DataStart, DataSize: LongWord);
  1589. begin
  1590. ReadMotorolaNumber(AStream, @FNumber, 2);
  1591. ReadMotorolaNumber(AStream, @FFlags, 2);
  1592. ReadMotorolaNumber(AStream, @FPivot, 4, 3);
  1593. ReadS0(AStream, FName);
  1594. if ((DataStart + DataSize) - Cardinal(AStream.Position)) > 2 then
  1595. ReadMotorolaNumber(AStream, @FParent, 2);
  1596. end;
  1597. (*********************************** TLWSurf **********************************)
  1598. destructor TLWSurf.Destroy;
  1599. begin
  1600. inherited;
  1601. end;
  1602. class function TLWSurf.GetID: TID4;
  1603. begin
  1604. result := ID_SURF;
  1605. end;
  1606. function TLWSurf.GetParamAddr(Param: TID4): Pointer;
  1607. var
  1608. Idx: Integer;
  1609. sParam: string;
  1610. begin
  1611. result := inherited GetParamAddr(Param);
  1612. if (result = nil) and (Source <> '') then
  1613. begin
  1614. sParam := string(Param);
  1615. Idx := RootChunks.FindChunk(@FindSurfaceByName, @sParam, 0);
  1616. if Idx <> -1 then
  1617. result := TLWSurf(RootChunks[Idx]).ParamAddr[Param];
  1618. end;
  1619. end;
  1620. function TLWSurf.GetSurfId: Integer;
  1621. var
  1622. c, SurfIdx: Integer;
  1623. begin
  1624. c := 0;
  1625. SurfIdx := Owner.FindChunk(@FindChunkById, @ID_SURF);
  1626. while (SurfIdx <> -1) and (Owner[SurfIdx] <> Self) do
  1627. begin
  1628. SurfIdx := Owner.FindChunk(@FindChunkById, @ID_SURF, SurfIdx + 1);
  1629. Inc(c);
  1630. end;
  1631. result := c;
  1632. end;
  1633. procedure TLWSurf.LoadData(AStream: TStream; DataStart, DataSize: LongWord);
  1634. var
  1635. CurId: TID4;
  1636. begin
  1637. ReadS0(AStream, FName);
  1638. ReadS0(AStream, FSource);
  1639. while Cardinal(AStream.Position) < (DataStart + DataSize) do
  1640. begin
  1641. AStream.Read(CurId, 4);
  1642. Items.Add(GetChunkClass(CurId, TLWSubChunk).Create);
  1643. {$IFDEF WIN32}
  1644. with Items[Items.Count - 1] do
  1645. {$ELSE}
  1646. /// with Items[Items.Count - 1] do
  1647. {$ENDIF}
  1648. begin
  1649. FID := CurId;
  1650. LoadFromStream(AStream);
  1651. end;
  1652. end;
  1653. end;
  1654. (*********************************** TLWPTag **********************************)
  1655. constructor TLWPTag.Create;
  1656. begin
  1657. inherited;
  1658. end;
  1659. function TLWPTag.AddTag(Value: TU2): Integer;
  1660. var
  1661. i, L: Integer;
  1662. begin
  1663. result := -1;
  1664. L := Length(FTags);
  1665. for i := 0 to L - 1 do
  1666. if Value = FTags[i] then
  1667. begin
  1668. result := i;
  1669. Exit;
  1670. end;
  1671. if result = -1 then
  1672. begin
  1673. SetLength(FTags, L + 1);
  1674. FTags[L] := Value;
  1675. result := L;
  1676. end;
  1677. end;
  1678. procedure TLWPTag.Clear;
  1679. begin
  1680. SetLength(FTagMaps, 0);
  1681. SetLength(FTags, 0);
  1682. end;
  1683. function TLWPTag.GetPolsByTag(tag: TU2; var PolyIndices: TU2DynArray): Integer;
  1684. var
  1685. i: Integer;
  1686. procedure AddPoly(Value: TU2);
  1687. var
  1688. L: Integer;
  1689. begin
  1690. L := Length(PolyIndices);
  1691. SetLength(PolyIndices, L + 1);
  1692. PolyIndices[L] := Value;
  1693. end;
  1694. begin
  1695. for i := 0 to TagMapCount - 1 do
  1696. if TagMaps[i].tag = tag then
  1697. AddPoly(TagMaps[i].poly);
  1698. result := Length(PolyIndices);
  1699. end;
  1700. class function TLWPTag.GetID: TID4;
  1701. begin
  1702. result := ID_PTAG;
  1703. end;
  1704. function TLWPTag.GetTag(AIndex: Integer): TU2;
  1705. begin
  1706. ValidateTagInfo;
  1707. result := FTags[AIndex];
  1708. end;
  1709. function TLWPTag.GetTagCount: Integer;
  1710. begin
  1711. ValidateTagInfo;
  1712. result := Length(FTags);
  1713. end;
  1714. function TLWPTag.GetTagMapCount: Integer;
  1715. begin
  1716. result := Length(FTagMaps) div 2;
  1717. end;
  1718. function TLWPTag.GetTagMaps(AIndex: Integer): TLWPolyTagMap;
  1719. begin
  1720. result := PLWPolyTagMap(@FTagMaps[AIndex * 2])^;
  1721. end;
  1722. procedure TLWPTag.LoadData(AStream: TStream; DataStart, DataSize: LongWord);
  1723. var
  1724. Idx: Integer;
  1725. begin
  1726. Idx := 0;
  1727. with AStream do
  1728. begin
  1729. Read(FMapType, 4);
  1730. SetLength(FTagMaps, (DataSize - 4) div 2);
  1731. while Cardinal(Position) < (DataStart + DataSize) do
  1732. begin
  1733. ReadVXAsU2(AStream, @FTagMaps[Idx]);
  1734. ReadMotorolaNumber(AStream, @FTagMaps[Idx + 1], 2);
  1735. Inc(Idx, 2);
  1736. end;
  1737. // correct length guestimate errors if any
  1738. if (Idx + 1) < Length(FTagMaps) then
  1739. SetLength(FTagMaps, Idx + 1);
  1740. end;
  1741. end;
  1742. procedure TLWPTag.ValidateTagInfo;
  1743. var
  1744. i: Integer;
  1745. begin
  1746. if Length(FTags) > 0 then
  1747. Exit;
  1748. for i := 0 to TagMapCount - 1 do
  1749. AddTag(TagMaps[i].tag);
  1750. end;
  1751. (******************************** TLWParentChunk ******************************)
  1752. procedure TLWParentChunk.Clear;
  1753. begin
  1754. FreeAndNil(FItems);
  1755. inherited;
  1756. end;
  1757. function TLWParentChunk.GetFloatParam(Param: TID4): Single;
  1758. var
  1759. pdata: Pointer;
  1760. begin
  1761. pdata := ParamAddr[Param];
  1762. if pdata <> nil then
  1763. begin
  1764. result := PF4(pdata)^;
  1765. ReverseByteOrder(@result, 4);
  1766. end
  1767. else
  1768. result := 0.0;
  1769. end;
  1770. function TLWParentChunk.GetItems: TLWChunkList;
  1771. begin
  1772. if FItems = nil then
  1773. FItems := TLWChunkList.Create(true, Self);
  1774. result := FItems;
  1775. end;
  1776. function TLWParentChunk.GetLongParam(Param: TID4): LongWord;
  1777. var
  1778. pdata: Pointer;
  1779. begin
  1780. pdata := ParamAddr[Param];
  1781. if pdata <> nil then
  1782. begin
  1783. result := PU4(pdata)^;
  1784. ReverseByteOrder(@result, 4);
  1785. end
  1786. else
  1787. result := 0;
  1788. end;
  1789. function TLWParentChunk.GetParamAddr(Param: TID4): Pointer;
  1790. var
  1791. Idx: Integer;
  1792. begin
  1793. result := nil;
  1794. Idx := Items.FindChunk(@FindChunkById, @Param, 0);
  1795. if Idx <> -1 then
  1796. result := Items[Idx].data;
  1797. end;
  1798. function TLWPols.GetPolsByPntIdx(VertIdx: TU2;
  1799. var VertPolys: TU2DynArray): Integer;
  1800. var
  1801. i, j, L: Integer;
  1802. begin
  1803. L := 0;
  1804. if Length(VertPolys) > 0 then
  1805. SetLength(VertPolys, 0);
  1806. for i := 0 to PolsCount - 1 do
  1807. begin
  1808. for j := 1 to Indices[PolsByIndex[i]] do
  1809. begin
  1810. if Indices[PolsByIndex[i] + j] = VertIdx then
  1811. begin
  1812. L := Length(VertPolys);
  1813. SetLength(VertPolys, L + 1);
  1814. VertPolys[L] := i;
  1815. end;
  1816. end;
  1817. end;
  1818. result := L;
  1819. end;
  1820. function TLWChunkList.Add(AChunk: TLWChunk): Integer;
  1821. begin
  1822. if (FOwner <> nil) and (FOwner is TLWParentChunk) then
  1823. AChunk.FParentChunk := TLWParentChunk(FOwner);
  1824. AChunk.FOwner := Self;
  1825. result := inherited Add(AChunk);
  1826. end;
  1827. procedure TLWPols.CalcPntsNormals;
  1828. var
  1829. i, j, k, PntIdx, PolyIdx, SurfIdx: Integer;
  1830. Pnts: TLWPnts;
  1831. // PTags: TLWPTag;
  1832. TmpAddr: Pointer;
  1833. sman: TF4;
  1834. begin
  1835. // Todo: CalcPntsNormals
  1836. if IndiceCount = 0 then
  1837. Exit;
  1838. with ParentChunk as TLWLayr do
  1839. Pnts := TLWPnts(Items[Items.FindChunk(@FindChunkById, @ID_PNTS, 0)]);
  1840. for PolyIdx := 0 to PolsCount - 1 do
  1841. begin
  1842. i := PolsByIndex[PolyIdx];
  1843. SurfIdx := RootChunks.FindChunk(@FindSurfaceByTag,
  1844. @FPolsInfo[PolyIdx].surfid);
  1845. TmpAddr := TLWSurf(RootChunks[SurfIdx]).ParamAddr[ID_SMAN];
  1846. if TmpAddr <> nil then
  1847. begin
  1848. sman := PF4(TmpAddr)^;
  1849. ReverseByteOrder(@sman, 4);
  1850. end
  1851. else
  1852. sman := 0;
  1853. for j := 1 to Indices[i] do
  1854. begin
  1855. FPolsInfo[PolyIdx].vnorms[j - 1] := FPolsInfo[PolyIdx].norm;
  1856. if sman <= 0 then
  1857. continue;
  1858. PntIdx := Indices[i + j];
  1859. for k := 0 to Pnts.PntsInfo[PntIdx].npols - 1 do
  1860. begin
  1861. if Pnts.PntsInfo[PntIdx].pols[k] = PolyIdx then
  1862. continue;
  1863. if ArcCos(VecDot(FPolsInfo[PolyIdx].norm,
  1864. FPolsInfo[Pnts.PntsInfo[PntIdx].pols[k]].norm)) > sman then
  1865. continue;
  1866. FPolsInfo[PolyIdx].vnorms[j - 1] :=
  1867. VecAdd(FPolsInfo[PolyIdx].vnorms[j - 1],
  1868. FPolsInfo[Pnts.PntsInfo[PntIdx].pols[k]].norm);
  1869. end;
  1870. FPolsInfo[PolyIdx].vnorms[j - 1] :=
  1871. VecNorm(FPolsInfo[PolyIdx].vnorms[j - 1]);
  1872. end;
  1873. end;
  1874. end;
  1875. function TLWChunk.GetRootChunks: TLWChunkList;
  1876. var
  1877. Parent: TLWParentChunk;
  1878. begin
  1879. result := nil;
  1880. if (FParentChunk = nil) then
  1881. begin
  1882. if (FOwner is TLWChunkList) then
  1883. begin
  1884. result := FOwner;
  1885. Exit;
  1886. end;
  1887. end
  1888. else
  1889. begin
  1890. Parent := FParentChunk;
  1891. while not(Parent.ParentChunk = nil) do
  1892. Parent := Parent.ParentChunk;
  1893. result := Parent.Owner;
  1894. end;
  1895. end;
  1896. function TLWChunkList.FindChunk(ChunkFind: TLWChunkFind; Criteria: Pointer;
  1897. StartIndex: Integer): Integer;
  1898. var
  1899. Found: boolean;
  1900. begin
  1901. Found := false;
  1902. result := -1;
  1903. while (StartIndex < Count) and (not Found) do
  1904. begin
  1905. ChunkFind(Items[StartIndex], Criteria, Found);
  1906. if Found then
  1907. begin
  1908. result := StartIndex;
  1909. Exit;
  1910. end;
  1911. Inc(StartIndex);
  1912. end;
  1913. end;
  1914. function TLWChunk.GetIndex: Integer;
  1915. begin
  1916. result := Owner.IndexOf(Self);
  1917. end;
  1918. procedure TLWChunk.Loaded;
  1919. begin
  1920. // do nothing
  1921. end;
  1922. procedure TLWChunkList.Loaded;
  1923. var
  1924. i: Integer;
  1925. begin
  1926. for i := 0 to Count - 1 do
  1927. begin
  1928. Items[i].Loaded;
  1929. end;
  1930. end;
  1931. function TLWParentChunk.GetVec3Param(Param: TID4): TVec12;
  1932. var
  1933. pdata: Pointer;
  1934. begin
  1935. pdata := ParamAddr[Param];
  1936. if pdata <> nil then
  1937. begin
  1938. result := PVec12(pdata)^;
  1939. ReverseByteOrder(@result, 4, 3);
  1940. end
  1941. else
  1942. begin
  1943. result[0] := 0;
  1944. result[1] := 1;
  1945. result[2] := 2;
  1946. end;
  1947. end;
  1948. function TLWParentChunk.GetVXParam(Param: TID4): Word;
  1949. var
  1950. pdata: Pointer;
  1951. begin
  1952. pdata := ParamAddr[Param];
  1953. if pdata <> nil then
  1954. result := ValueOfVX(pdata)
  1955. else
  1956. result := 0;
  1957. end;
  1958. function TLWParentChunk.GetWordParam(Param: TID4): Word;
  1959. var
  1960. pdata: Pointer;
  1961. begin
  1962. pdata := ParamAddr[Param];
  1963. if pdata <> nil then
  1964. begin
  1965. result := PU4(pdata)^;
  1966. ReverseByteOrder(@result, 2);
  1967. end
  1968. else
  1969. result := 0;
  1970. end;
  1971. procedure TLWParentChunk.Loaded;
  1972. begin
  1973. Items.Loaded;
  1974. end;
  1975. procedure TLWPols.Loaded;
  1976. begin
  1977. inherited;
  1978. CalcPntsNormals;
  1979. end;
  1980. function TLWObjectFile.TagToName(tag: TU2): string;
  1981. var
  1982. TagsIdx: Integer;
  1983. begin
  1984. TagsIdx := Chunks.FindChunk(@FindChunkById, @ID_TAGS);
  1985. if TagsIdx <> -1 then
  1986. result := TLWTags(Chunks[TagsIdx]).TagToName(tag);
  1987. end;
  1988. (******************** TLWClip ********************)
  1989. class function TLWClip.GetID: TID4;
  1990. begin
  1991. result := ID_CLIP;
  1992. end;
  1993. procedure TLWClip.LoadData(AStream: TStream; DataStart, DataSize: LongWord);
  1994. var
  1995. CurId: TID4;
  1996. begin
  1997. ReadMotorolaNumber(AStream, @FClipIndex, 4);
  1998. while Cardinal(AStream.Position) < (DataStart + DataSize) do
  1999. begin
  2000. AStream.Read(CurId, 4);
  2001. Items.Add(GetChunkClass(CurId, TLWSubChunk).Create);
  2002. {$IFDEF WIN32}
  2003. with Items[Items.Count - 1] do
  2004. {$ELSE}
  2005. /// with Items[Items.Count - 1] do
  2006. {$ENDIF}
  2007. begin
  2008. FID := CurId;
  2009. LoadFromStream(AStream);
  2010. end;
  2011. end;
  2012. end;
  2013. // TLWContentDir
  2014. (* function TLWContentDir.ContentSearch(AFilename: string): string;
  2015. var
  2016. i: Integer;
  2017. begin
  2018. if not FileExists(AFilename) then
  2019. begin
  2020. result := ExtractFileName(AFilename);
  2021. if not FileExists(result) then
  2022. begin
  2023. for i := 0 to SubDirs.Count - 1 do
  2024. begin
  2025. if FileExists(Root+'\'+SubDirs[i]+'\'+result) then
  2026. begin
  2027. result:=Root+'\'+SubDirs[i]+'\'+result;
  2028. Exit;
  2029. end;
  2030. end;
  2031. result := '';
  2032. end;
  2033. end;
  2034. end;
  2035. *)
  2036. destructor TLWContentDir.Destroy;
  2037. begin
  2038. FreeAndNil(FSubDirs);
  2039. inherited;
  2040. end;
  2041. function TLWContentDir.FindContent(AFilename: string): string;
  2042. var
  2043. i: Integer;
  2044. begin
  2045. if not FileExists(AFilename) then
  2046. begin
  2047. result := ExtractFileName(AFilename);
  2048. if not FileExists(result) then
  2049. begin
  2050. for i := 0 to SubDirs.Count - 1 do
  2051. begin
  2052. if FileExists(Root + '\' + SubDirs[i] + '\' + result) then
  2053. begin
  2054. result := Root + '\' + SubDirs[i] + '\' + result;
  2055. Exit;
  2056. end;
  2057. end;
  2058. result := '';
  2059. end;
  2060. end;
  2061. end;
  2062. function TLWContentDir.GetSubDirs: TStrings;
  2063. begin
  2064. if FSubDirs = nil then
  2065. FSubDirs := TStringList.Create;
  2066. result := FSubDirs;
  2067. end;
  2068. procedure TLWContentDir.SetRoot(const Value: string);
  2069. begin
  2070. FRoot := Value;
  2071. end;
  2072. procedure TLWContentDir.SetSubDirs(const Value: TStrings);
  2073. begin
  2074. SubDirs.Assign(Value);
  2075. end;
  2076. //--------------------------------------------------------------------------
  2077. initialization
  2078. //--------------------------------------------------------------------------
  2079. // Pnts
  2080. RegisterChunkClass(TLWPnts);
  2081. // Pols
  2082. RegisterChunkClass(TLWPols);
  2083. // VMap
  2084. RegisterChunkClass(TLWVMap);
  2085. // Tags
  2086. RegisterChunkClass(TLWTags);
  2087. // PTAG
  2088. RegisterChunkClass(TLWPTag);
  2089. // SURF
  2090. RegisterChunkClass(TLWSurf);
  2091. // LAYR
  2092. RegisterChunkClass(TLWLayr);
  2093. // CLIP
  2094. RegisterChunkClass(TLWClip);
  2095. finalization
  2096. // UnRegisterChunkClasses;
  2097. FreeAndNil(ChunkClasses);
  2098. FreeAndNil(ContentDir);
  2099. end.