Formatx.LWO.pas 59 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465
  1. //
  2. // The graphics engine GXScene
  3. //
  4. unit Formatx.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. Stage.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. implementation // ------------------------------------------------------------
  681. type
  682. PWord = ^Word;
  683. PLongWord = ^LongWord;
  684. var
  685. ChunkClasses: TList;
  686. ContentDir: TLWContentDir;
  687. function ToDosPath(const Path: string): string;
  688. var
  689. i: Integer;
  690. begin
  691. result := Path;
  692. for i := 1 to Length(result) do
  693. if result[i] = '/' then
  694. result[i] := '\';
  695. end;
  696. function ToUnixPath(const Path: string): string;
  697. var
  698. i: Integer;
  699. begin
  700. result := Path;
  701. for i := 1 to Length(result) do
  702. if result[i] = '\' then
  703. result[i] := '/';
  704. end;
  705. function GetContentDir: TLWContentDir;
  706. begin
  707. if ContentDir = nil then
  708. ContentDir := TLWContentDir.Create;
  709. result := ContentDir;
  710. end;
  711. procedure FindChunkById(AChunk: TLWChunk; data: Pointer; var Found: boolean);
  712. begin
  713. if AChunk.FID = PID4(data)^ then
  714. Found := true
  715. else
  716. Found := false;
  717. end;
  718. procedure FindClipByClipIndex(AChunk: TLWChunk; AIndex: Pointer;
  719. var Found: boolean);
  720. begin
  721. if (AChunk is TLWClip) and (TLWClip(AChunk).ClipIndex = PU2(AIndex)^) then
  722. Found := true;
  723. end;
  724. procedure FindSurfaceByName(AChunk: TLWChunk; AName: Pointer;
  725. var Found: boolean);
  726. begin
  727. if (AChunk is TLWSurf) and (TLWSurf(AChunk).Name = PString(AName)^) then
  728. Found := true;
  729. end;
  730. procedure FindSurfaceByTag(AChunk: TLWChunk; ATag: Pointer; var Found: boolean);
  731. begin
  732. if (AChunk is TLWSurf) and (TLWSurf(AChunk).surfid = PU2(ATag)^) then
  733. Found := true;
  734. end;
  735. procedure FindVMapByName(AChunk: TLWChunk; AName: Pointer; var Found: boolean);
  736. begin
  737. if (AChunk is TLWVMap) and (TLWVMap(AChunk).Name = PString(AName)^) then
  738. Found := true;
  739. end;
  740. function VecAdd(v1, v2: TVec12): TVec12;
  741. begin
  742. result[0] := v1[0] + v2[0];
  743. result[1] := v1[1] + v2[1];
  744. result[2] := v1[2] + v2[2];
  745. end;
  746. function VecSub(v1, v2: TVec12): TVec12;
  747. begin
  748. result[0] := v1[0] - v2[0];
  749. result[1] := v1[1] - v2[1];
  750. result[2] := v1[2] - v2[2];
  751. end;
  752. function VecCross(v1, v2: TVec12): TVec12;
  753. begin
  754. result[0] := v1[1] * v2[2] - v1[2] * v2[1];
  755. result[1] := v1[2] * v2[0] - v1[0] * v2[2];
  756. result[2] := v1[0] * v2[1] - v1[1] * v2[0];
  757. end;
  758. function VecDot(v1, v2: TVec12): TF4;
  759. begin
  760. result := v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
  761. end;
  762. function VecNorm(v: TVec12): TVec12;
  763. var
  764. mag: TF4;
  765. begin
  766. mag := Sqrt(VecDot(v, v));
  767. if mag > 0 then
  768. mag := 1 / mag;
  769. result[0] := v[0] * mag;
  770. result[1] := v[1] * mag;
  771. result[2] := v[2] * mag;
  772. end;
  773. function CalcPlaneNormal(v1, v2, v3: TVec12): TVec12;
  774. var
  775. e1, e2: TVec12;
  776. begin
  777. e1 := VecSub(v2, v1);
  778. e2 := VecSub(v3, v1);
  779. result := VecCross(e1, e2);
  780. result := VecNorm(result);
  781. end;
  782. procedure FindSurfByName(Chunk: TLWChunk; var Found: boolean);
  783. begin
  784. end;
  785. (*-----------------------------------------------------------------------------
  786. Procedure: GetChunkClasses
  787. Date: 08-Aug-2002
  788. Arguments: None
  789. Result: TClassList
  790. Singleton access for the chunk class list.
  791. -----------------------------------------------------------------------------*)
  792. function GetChunkClasses: TList;
  793. begin
  794. if ChunkClasses = nil then
  795. ChunkClasses := TList.Create;
  796. result := ChunkClasses;
  797. end;
  798. procedure UnRegisterChunkClasses;
  799. var
  800. i: Integer;
  801. begin
  802. with GetChunkClasses do
  803. for i := 0 to Count - 1 do
  804. UnregisterClass(TPersistentClass(Items[i]));
  805. end;
  806. (*-----------------------------------------------------------------------------
  807. Procedure: RegisterChunkClass
  808. Date: 08-Aug-2002
  809. Arguments: ChunkClass: TLWChunkClass
  810. Result: None
  811. Adds a user defined chunk class to the chunk class list.
  812. -----------------------------------------------------------------------------*)
  813. procedure RegisterChunkClass(ChunkClass: TLWChunkClass);
  814. begin
  815. GetChunkClasses.Add(ChunkClass);
  816. // if FindClass(ChunkClass.ClassName) <> nil then
  817. // UnRegisterClass(ChunkClass);
  818. // RegisterClass(ChunkClass);
  819. end;
  820. (*-----------------------------------------------------------------------------
  821. Procedure: GetChunkClass
  822. Date: 08-Aug-2002
  823. Arguments: ChunkID: TID4
  824. Result: TLWChunkClass
  825. Returns the chunk class associated with ChunkID.
  826. -----------------------------------------------------------------------------*)
  827. function GetChunkClass(ChunkID: TID4; ADefault: TLWChunkClass): TLWChunkClass;
  828. var
  829. i: Integer;
  830. begin
  831. if ADefault = nil then
  832. result := TLWChunk
  833. else
  834. result := ADefault;
  835. for i := 0 to ChunkClasses.Count - 1 do
  836. begin
  837. if TLWChunkClass(ChunkClasses.Items[i]).GetID = ChunkID then
  838. begin
  839. result := TLWChunkClass(ChunkClasses.Items[i]);
  840. Exit;
  841. end;
  842. end;
  843. end;
  844. (*-----------------------------------------------------------------------------
  845. Procedure: Tokenize
  846. Date: 08-Aug-2002
  847. Arguments: const Src: string; Delimiter: Char; Dst: TStrings
  848. Result: None
  849. Breaks up a string into TStrings items when the Delimiter character is
  850. encountered.
  851. -----------------------------------------------------------------------------*)
  852. procedure Tokenize(const Src: string; Delimiter: Char; Dst: TStrings);
  853. var
  854. i, L, SL: Integer;
  855. SubStr: string;
  856. begin
  857. if Dst = nil then
  858. Exit;
  859. L := Length(Src);
  860. if (L = 0) or (Dst = nil) then
  861. Exit;
  862. SubStr := '';
  863. for i := 1 to L do
  864. begin
  865. if (Src[i] <> Delimiter) then
  866. SubStr := SubStr + Src[i]
  867. else
  868. begin
  869. SL := Length(SubStr);
  870. if SL > 0 then
  871. begin
  872. Dst.Add(SubStr);
  873. SubStr := '';
  874. end;
  875. end;
  876. end;
  877. if Length(SubStr) > 0 then
  878. Dst.Add(SubStr);
  879. end;
  880. (*-----------------------------------------------------------------------------
  881. Procedure: LoadLW0FromStream
  882. Date: 08-Aug-2002
  883. Arguments: Stream: TStream; ReadCallback: TLWOReadCallback; UserData: Pointer
  884. Result: LongWord
  885. -----------------------------------------------------------------------------*)
  886. function LoadLW0FromStream(Stream: TStream; ReadCallback: TLWOReadCallback;
  887. UserData: Pointer): LongWord;
  888. var
  889. Chunk: TLWChunkRec;
  890. CurId: TID4;
  891. StartPos, CurSize: TU4;
  892. begin
  893. try
  894. Stream.Read(CurId, 4);
  895. ReadMotorolaNumber(Stream, @CurSize, 4);
  896. if UpperCase(string(CurId)) = 'FORM' then
  897. begin
  898. Stream.Read(CurId, 4);
  899. end
  900. else
  901. raise Exception.Create
  902. ('Invalid magic number. Not a valid Lightwave Object');
  903. with Stream do
  904. while Position < size do
  905. begin
  906. Read(Chunk, 8);
  907. ReverseByteOrder(@Chunk.size, 4);
  908. StartPos := Position;
  909. GetMem(Chunk.data, Chunk.size);
  910. Stream.Read(Chunk.data^, Chunk.size);
  911. if Assigned(ReadCallback) then
  912. ReadCallback(Chunk, UserData);
  913. FreeMem(Chunk.data, Chunk.size);
  914. Position := StartPos + Chunk.size + (StartPos + Chunk.size) mod 2;
  915. end;
  916. Stream.Free;
  917. result := High(LongWord);
  918. except
  919. On E: Exception do
  920. begin
  921. Stream.Free;
  922. result := 0;
  923. end;
  924. end;
  925. end;
  926. function LoadLWOFromFile(const AFilename: String;
  927. ReadCallback: TLWOReadCallback; UserData: Pointer): LongWord;
  928. var
  929. Stream: TStream;
  930. begin
  931. Stream := TFileStream.Create(AFilename, fmOpenRead);
  932. try
  933. result := LoadLW0FromStream(Stream, ReadCallback, UserData);
  934. finally
  935. Stream.Free;
  936. end;
  937. end;
  938. procedure ReverseByteOrder(ValueIn: Pointer; size: Integer; Count: Integer = 1);
  939. var
  940. W: Word;
  941. pB: PByte;
  942. Blo, Bhi: Byte;
  943. L: LongWord;
  944. i: Integer;
  945. begin
  946. i := 0;
  947. case size of
  948. 2:
  949. begin
  950. while i < Count do
  951. begin
  952. W := PU2Array(ValueIn)^[i];
  953. pB := @W;
  954. Blo := pB^;
  955. Inc(pB);
  956. Bhi := pB^;
  957. pB^ := Blo;
  958. Dec(pB);
  959. pB^ := Bhi;
  960. PU2Array(ValueIn)^[i] := W;
  961. Inc(i);
  962. end;
  963. end;
  964. 4:
  965. begin
  966. while i < Count do
  967. begin
  968. L := PU4Array(ValueIn)^[i];
  969. pB := @W;
  970. Blo := pB^;
  971. Inc(pB);
  972. Bhi := pB^;
  973. pB^ := Blo;
  974. Dec(pB);
  975. pB^ := Bhi;
  976. PU4Array(ValueIn)^[i] := L;
  977. Inc(i);
  978. end;
  979. end;
  980. else
  981. raise Exception.Create('Lightwave.ReverseByteOrder: Invalid Size = ' +
  982. IntToStr(size));
  983. end;
  984. end;
  985. procedure ReadMotorolaNumber(Stream: TStream; data: Pointer;
  986. ElementSize: Integer; Count: Integer = 1);
  987. begin
  988. Stream.Read(data^, Count * ElementSize);
  989. if (ElementSize = 2) or (ElementSize = 4) then
  990. ReverseByteOrder(data, ElementSize, Count);
  991. end;
  992. function WriteMotorolaNumber(Stream: TStream; data: Pointer;
  993. ElementSize: Integer; Count: Integer = 1): Integer;
  994. var
  995. TempData: Pointer;
  996. begin
  997. result := 0;
  998. if data <> nil then
  999. begin
  1000. TempData := AllocMem(ElementSize * Count);
  1001. try
  1002. if (ElementSize = 2) or (ElementSize = 4) then
  1003. ReverseByteOrder(TempData, ElementSize, Count);
  1004. result := Stream.Write(data, Count * ElementSize);
  1005. except
  1006. on E: Exception do
  1007. begin
  1008. FreeMem(TempData, Count * ElementSize);
  1009. raise;
  1010. end;
  1011. end;
  1012. end;
  1013. end;
  1014. function ReadS0(Stream: TStream; out Str: string): Integer;
  1015. var
  1016. Buf: array [0 .. 1] of AnsiChar;
  1017. StrBuf: string;
  1018. begin
  1019. Stream.Read(Buf, 2);
  1020. StrBuf := '';
  1021. while Buf[1] <> #0 do
  1022. begin
  1023. StrBuf := StrBuf + string(Buf);
  1024. Stream.Read(Buf, 2);
  1025. end;
  1026. if Buf[0] <> #0 then
  1027. StrBuf := StrBuf + Char(Buf[0]);
  1028. Str := Copy(StrBuf, 1, Length(StrBuf));
  1029. result := Length(Str) + 1;
  1030. result := result + (result mod 2);
  1031. end;
  1032. function ValueOfVX(VX: Pointer): TU4;
  1033. var
  1034. TmpU2: TU2;
  1035. TmpU4: TU4;
  1036. begin
  1037. if PU1(VX)^ = $FF then
  1038. begin
  1039. TmpU4 := TU4(PU1(VX)^) and $FFFFFFF0;
  1040. ReverseByteOrder(@TmpU4, 4);
  1041. end
  1042. else
  1043. begin
  1044. TmpU2 := TU2(PU2(VX)^);
  1045. ReverseByteOrder(@TmpU2, 2);
  1046. TmpU4 := TmpU2;
  1047. end;
  1048. result := TmpU4;
  1049. end;
  1050. function ReadVXAsU4(Stream: TStream; data: Pointer; Count: Integer = 1)
  1051. : Integer;
  1052. var
  1053. i, ReadCount: Integer;
  1054. BufByte: Byte;
  1055. TempU2: TU2;
  1056. begin
  1057. ReadCount := 0;
  1058. for i := 0 to Count - 1 do
  1059. begin
  1060. Stream.Read(BufByte, 1);
  1061. Stream.Position := Stream.Position - 1;
  1062. if BufByte = 255 then
  1063. begin
  1064. Stream.Read(data^, SizeOf(TU4));
  1065. PU4Array(data)^[i] := PU4Array(data)^[i] and $FFFFFFF0;
  1066. ReverseByteOrder(data, SizeOf(TU4));
  1067. Inc(ReadCount, 4);
  1068. end
  1069. else
  1070. begin
  1071. Stream.Read(TempU2, SizeOf(TU2));
  1072. ReverseByteOrder(@TempU2, SizeOf(TU2));
  1073. PU4Array(data)^[i] := TempU2;
  1074. Inc(ReadCount, 2);
  1075. end;
  1076. end;
  1077. result := ReadCount;
  1078. end;
  1079. function ReadVXAsU2(Stream: TStream; data: Pointer; Count: Integer = 1)
  1080. : Integer;
  1081. var
  1082. i, ReadCount: Integer;
  1083. BufByte: Byte;
  1084. TempU2: TU2;
  1085. begin
  1086. ReadCount := 0;
  1087. for i := 0 to Count - 1 do
  1088. begin
  1089. Stream.Read(BufByte, 1);
  1090. Stream.Position := Stream.Position - 1;
  1091. if BufByte = 255 then
  1092. begin
  1093. Stream.Position := Stream.Position + 4;
  1094. PU2Array(data)^[i] := 0;
  1095. Inc(ReadCount, 4);
  1096. end
  1097. else
  1098. begin
  1099. Stream.Read(TempU2, SizeOf(TU2));
  1100. ReverseByteOrder(@TempU2, SizeOf(TU2));
  1101. PU2Array(data)^[i] := TempU2;
  1102. Inc(ReadCount, 2);
  1103. end;
  1104. end;
  1105. result := ReadCount;
  1106. end;
  1107. procedure WriteS0(Stream: TStream; data: string);
  1108. begin
  1109. // ToDo: WriteS0
  1110. end;
  1111. procedure WriteU4AsVX(Stream: TStream; data: Pointer; Count: Integer);
  1112. var
  1113. i: Integer;
  1114. TempU2: TU2;
  1115. begin
  1116. for i := 0 to Count - 1 do
  1117. begin
  1118. if PU4Array(data)^[i] < 65280 then
  1119. begin
  1120. TempU2 := PU4Array(data)^[i];
  1121. WriteMotorolaNumber(Stream, @TempU2, SizeOf(TU2));
  1122. end
  1123. else
  1124. WriteMotorolaNumber(Stream, data, SizeOf(TU4));
  1125. end;
  1126. end;
  1127. type
  1128. PInteger = ^Integer;
  1129. function ID4ToInt(const id: TID4): Integer;
  1130. var
  1131. TmpId: AnsiString;
  1132. begin
  1133. TmpId := id;
  1134. TmpId := AnsiString(UpperCase(string(id)));
  1135. result := PInteger(@TmpId)^;
  1136. end;
  1137. (*********************************** TLWChunk ********************************)
  1138. destructor TLWChunk.Destroy;
  1139. begin
  1140. Clear;
  1141. inherited;
  1142. end;
  1143. procedure TLWChunk.Clear;
  1144. begin
  1145. FreeMem(FData, FSize);
  1146. FSize := 0;
  1147. FData := nil;
  1148. end;
  1149. class function TLWChunk.GetID: TID4;
  1150. begin
  1151. result := #0#0#0#0;
  1152. end;
  1153. procedure TLWChunk.LoadData(AStream: TStream; DataStart, DataSize: LongWord);
  1154. begin
  1155. GetMem(FData, DataSize);
  1156. AStream.Read(PByteArray(FData)^[0], DataSize);
  1157. end;
  1158. procedure TLWChunk.LoadFromStream(AStream: TStream);
  1159. var
  1160. DataStart: Integer;
  1161. DataSize: TU4;
  1162. begin
  1163. with AStream do
  1164. begin
  1165. ReadMotorolaNumber(AStream, @DataSize, 4);
  1166. DataStart := Position;
  1167. FSize := DataSize;
  1168. LoadData(AStream, DataStart, DataSize);
  1169. Position := Cardinal(DataStart) + DataSize +
  1170. (Cardinal(DataStart) + DataSize) mod 2;
  1171. end;
  1172. end;
  1173. (********************************* TLWChunkList *******************************)
  1174. constructor TLWChunkList.Create(AOwnsItems: boolean; AOwner: TObject);
  1175. begin
  1176. inherited Create;
  1177. FOwnsItems := AOwnsItems;
  1178. FOwner := AOwner;
  1179. end;
  1180. destructor TLWChunkList.Destroy;
  1181. begin
  1182. Clear;
  1183. inherited;
  1184. end;
  1185. procedure TLWChunkList.Clear;
  1186. begin
  1187. while Count > 0 do
  1188. Delete(Count - 1);
  1189. inherited;
  1190. end;
  1191. procedure TLWChunkList.Delete(Index: Integer);
  1192. begin
  1193. if FOwnsItems then
  1194. Items[Index].Free;
  1195. inherited Delete(Index);
  1196. end;
  1197. function TLWChunkList.GetItem(Index: Integer): TLWChunk;
  1198. begin
  1199. result := TLWChunk(inherited Items[Index]);
  1200. end;
  1201. (******************************** TLWObjectFile *******************************)
  1202. constructor TLWObjectFile.Create;
  1203. begin
  1204. inherited;
  1205. end;
  1206. destructor TLWObjectFile.Destroy;
  1207. begin
  1208. FreeAndNil(FChunks);
  1209. inherited;
  1210. end;
  1211. function TLWObjectFile.GetChunks: TLWChunkList;
  1212. begin
  1213. if FChunks = nil then
  1214. FChunks := TLWChunkList.Create(true, Self);
  1215. result := FChunks;
  1216. end;
  1217. function TLWObjectFile.GetCount: Integer;
  1218. begin
  1219. result := Chunks.Count;
  1220. end;
  1221. function TLWObjectFile.GetSurfaceByName(Index: string): TLWSurf;
  1222. var
  1223. SurfIdx: Integer;
  1224. begin
  1225. SurfIdx := Chunks.FindChunk(@FindSurfaceByName, @Index, 0);
  1226. if SurfIdx <> -1 then
  1227. result := TLWSurf(Chunks[SurfIdx])
  1228. else
  1229. result := nil;
  1230. end;
  1231. function TLWObjectFile.GetSurfaceByTag(Index: TU2): TLWSurf;
  1232. var
  1233. TagName: string;
  1234. begin
  1235. TagName := TagToName(Index);
  1236. result := SurfaceByName[TagName];
  1237. end;
  1238. procedure TLWObjectFile.LoadFromFile(const AFilename: string);
  1239. var
  1240. Stream: TMemoryStream;
  1241. begin
  1242. Stream := TMemoryStream.Create;
  1243. try
  1244. Stream.LoadFromFile(AFilename);
  1245. LoadFromStream(Stream);
  1246. Stream.Free;
  1247. FFileName := AFilename;
  1248. except
  1249. on E: Exception do
  1250. begin
  1251. Stream.Free;
  1252. raise;
  1253. end;
  1254. end;
  1255. end;
  1256. procedure TLWObjectFile.LoadFromStream(AStream: TStream);
  1257. var
  1258. CurId: TID4;
  1259. CurSize: LongWord;
  1260. CurPnts, CurPols, CurItems: TLWChunkList;
  1261. begin
  1262. CurPols := nil;
  1263. CurPnts := nil;
  1264. AStream.Read(CurId, 4);
  1265. ReadMotorolaNumber(AStream, @CurSize, 4);
  1266. if UpperCase(string(CurId)) = 'FORM' then
  1267. begin
  1268. AStream.Read(CurId, 4);
  1269. if CurId <> 'LWO2' then
  1270. raise Exception.Create
  1271. ('Only Version 6.0+ version objects are supported.');
  1272. end
  1273. else
  1274. raise Exception.Create
  1275. ('Invalid magic number. Not a valid Lightwave Object');
  1276. CurItems := Chunks;
  1277. while AStream.Position < AStream.size do
  1278. begin
  1279. AStream.Read(CurId, 4);
  1280. if (CurId = ID_PTAG) then
  1281. begin
  1282. CurPols.Add(GetChunkClass(CurId, TLWChunk).Create);
  1283. {$IFDEF WIN32}
  1284. CurPols[CurPols.Count - 1].FID := CurId;
  1285. {$ELSE}
  1286. // CurPols[CurPols.Count - 1].FID := CurId;
  1287. {$ENDIF}
  1288. LoadFromStream(AStream);
  1289. end
  1290. else if (CurId = ID_VMAP) or (CurId = ID_VMAD) then
  1291. begin
  1292. CurPnts.Add(GetChunkClass(CurId, TLWChunk).Create);
  1293. {$IFDEF WIN32}
  1294. CurPnts[CurPnts.Count - 1].FID := CurId;
  1295. {$ELSE}
  1296. // CurPnts[CurPnts.Count - 1].FID := CurId;
  1297. {$ENDIF}
  1298. LoadFromStream(AStream);
  1299. end
  1300. else
  1301. begin
  1302. if (CurId = ID_LAYR) or (CurId = ID_SURF) or (CurId = ID_TAGS) or
  1303. (CurId = ID_CLIP) then
  1304. CurItems := Chunks;
  1305. CurItems.Add(GetChunkClass(CurId, TLWChunk).Create);
  1306. {$IFDEF WIN32}
  1307. CurItems[CurItems.Count - 1].FID := CurId;
  1308. {$ELSE}
  1309. // CurItems[CurItems.Count - 1].FID := CurId;
  1310. {$ENDIF}
  1311. LoadFromStream(AStream);
  1312. end;
  1313. if CurId = ID_LAYR then
  1314. CurItems := TLWParentChunk(CurItems[CurItems.Count - 1]).Items
  1315. else if CurId = ID_POLS then
  1316. CurPols := TLWParentChunk(CurItems[CurItems.Count - 1]).Items
  1317. else if CurId = ID_PNTS then
  1318. CurPnts := TLWParentChunk(CurItems[CurItems.Count - 1]).Items;
  1319. end;
  1320. Chunks.Loaded;
  1321. end;
  1322. (*********************************** TLWPnts **********************************)
  1323. function TLWPnts.AddPoly(PntIdx, PolyIdx: Integer): Integer;
  1324. var
  1325. i, L: Integer;
  1326. begin
  1327. // DONE: Pnts.AddPoly
  1328. for i := 0 to FPntsInfo[PntIdx].npols - 1 do
  1329. begin
  1330. if FPntsInfo[PntIdx].pols[i] = PolyIdx then
  1331. begin
  1332. result := i;
  1333. Exit;
  1334. end;
  1335. end;
  1336. L := Length(FPntsInfo[PntIdx].pols);
  1337. SetLength(FPntsInfo[PntIdx].pols, L + 1);
  1338. FPntsInfo[PntIdx].npols := L + 1;
  1339. FPntsInfo[PntIdx].pols[L] := PolyIdx;
  1340. result := L;
  1341. end;
  1342. procedure TLWPnts.Clear;
  1343. var
  1344. i: Integer;
  1345. begin
  1346. for i := 0 to PntsCount - 1 do
  1347. SetLength(FPntsInfo[i].pols, 0);
  1348. SetLength(FPntsInfo, 0);
  1349. SetLength(FPnts, 0);
  1350. end;
  1351. function TLWPnts.GetPntsCount: LongWord;
  1352. begin
  1353. result := Length(FPnts);
  1354. end;
  1355. class function TLWPnts.GetID: TID4;
  1356. begin
  1357. result := ID_PNTS;
  1358. end;
  1359. function TLWPnts.GetVMap(VMapID: TID4; out VMap: TLWVMap): boolean;
  1360. var
  1361. i: Integer;
  1362. begin
  1363. result := false;
  1364. for i := 0 to Items.Count - 1 do
  1365. begin
  1366. if (Items[i] is TLWVMap) and (TLWVMap(Items[i]).VMapType = VMapID) then
  1367. begin
  1368. result := true;
  1369. VMap := TLWVMap(Items[i]);
  1370. Exit;
  1371. end;
  1372. end;
  1373. end;
  1374. procedure TLWPnts.LoadData(AStream: TStream; DataStart, DataSize: LongWord);
  1375. begin
  1376. SetLength(FPnts, DataSize div 12);
  1377. // allocate storage for DataSize div 12 points
  1378. SetLength(FPntsInfo, DataSize div 12); // Point info
  1379. ReadMotorolaNumber(AStream, @FPnts[0], 4, DataSize div 4);
  1380. // read the point data
  1381. end;
  1382. (*********************************** TLWPols **********************************)
  1383. procedure TLWPols.CalcPolsNormals;
  1384. var
  1385. i, j, PolyIdx: Integer;
  1386. Pnts: TLWPnts;
  1387. begin
  1388. if IndiceCount = 0 then
  1389. Exit;
  1390. with ParentChunk as TLWLayr do
  1391. Pnts := TLWPnts(Items[Items.FindChunk(@FindChunkById, @ID_PNTS, 0)]);
  1392. for PolyIdx := 0 to FPolsCount - 1 do
  1393. begin
  1394. // DONE: call Pnts.AddPoly
  1395. i := PolsByIndex[PolyIdx];
  1396. with Pnts do
  1397. begin
  1398. for j := 1 to Indices[i] do
  1399. AddPoly(Indices[i + j], PolyIdx);
  1400. SetLength(FPolsInfo[PolyIdx].vnorms, Indices[i]);
  1401. if Indices[PolyIdx] > 2 then
  1402. FPolsInfo[PolyIdx].norm := CalcPlaneNormal(Pnts[Indices[i + 1]],
  1403. Pnts[Indices[i + 2]], Pnts[Indices[i + 3]])
  1404. else
  1405. FPolsInfo[PolyIdx].norm := VecNorm(Pnts[Indices[i + 1]]);
  1406. end;
  1407. end;
  1408. end;
  1409. procedure TLWPols.Clear;
  1410. var
  1411. i: Integer;
  1412. begin
  1413. for i := 0 to FPolsCount - 1 do
  1414. SetLength(FPolsInfo[i].vnorms, 0);
  1415. SetLength(FPolsInfo, 0);
  1416. SetLength(FPols, 0);
  1417. end;
  1418. function TLWPols.GetPolsByIndex(AIndex: TU2): Integer;
  1419. var
  1420. i, cnt: Cardinal;
  1421. begin
  1422. result := -1;
  1423. i := 0;
  1424. cnt := 0;
  1425. if AIndex = 0 then
  1426. begin
  1427. result := 0;
  1428. Exit;
  1429. end;
  1430. while (i < IndiceCount - 1) and (cnt <> AIndex) do
  1431. begin
  1432. Inc(i, Indices[i] + 1);
  1433. Inc(cnt);
  1434. end;
  1435. if cnt = AIndex then
  1436. result := i;
  1437. end;
  1438. class function TLWPols.GetID: TID4;
  1439. begin
  1440. result := ID_POLS;
  1441. end;
  1442. function TLWPols.GetIndiceCount: TU4;
  1443. begin
  1444. result := Length(FPols);
  1445. end;
  1446. function TLWPols.GetIndice(AIndex: Integer): TU2;
  1447. begin
  1448. result := FPols[AIndex];
  1449. end;
  1450. function TLWPols.GetPolsCount: Integer;
  1451. begin
  1452. result := FPolsCount;
  1453. end;
  1454. procedure TLWPols.LoadData(AStream: TStream; DataStart, DataSize: LongWord);
  1455. var
  1456. EndPos: Integer;
  1457. Idx: TU4;
  1458. TmpU2: TU2;
  1459. begin
  1460. Idx := 0;
  1461. EndPos := DataStart + DataSize;
  1462. with AStream do
  1463. begin
  1464. Read(FPolsType, 4);
  1465. // To avoid memory manager hits, set an estimate length of indices
  1466. SetLength(FPols, (DataSize - 4) div 2);
  1467. while Position < EndPos do
  1468. begin
  1469. ReadMotorolaNumber(AStream, @FPols[Idx], 2);
  1470. TmpU2 := FPols[Idx] and POLS_VCOUNT_MASK;
  1471. ReadVXAsU2(AStream, @FPols[Idx + 1], TmpU2);
  1472. Inc(Idx, FPols[Idx] + 1);
  1473. Inc(FPolsCount);
  1474. end;
  1475. // correct length estimate errors if any
  1476. if (Idx + 1) < Cardinal(Length(FPols)) then
  1477. SetLength(FPols, Idx + 1);
  1478. end;
  1479. SetLength(FPolsInfo, FPolsCount);
  1480. CalcPolsNormals;
  1481. end;
  1482. (*********************************** TLWVMap **********************************)
  1483. procedure TLWVMap.Clear;
  1484. var
  1485. i: Integer;
  1486. begin
  1487. for i := 0 to Length(FValues) - 1 do
  1488. SetLength(FValues[i].values, 0);
  1489. SetLength(FValues, 0);
  1490. end;
  1491. class function TLWVMap.GetID: TID4;
  1492. begin
  1493. result := ID_VMAP;
  1494. end;
  1495. function TLWVMap.GetValue(AIndex: TU2): TLWVertexMap;
  1496. begin
  1497. result := FValues[AIndex];
  1498. end;
  1499. function TLWVMap.GetValueCount: Integer;
  1500. begin
  1501. result := Length(FValues);
  1502. end;
  1503. procedure TLWVMap.LoadData(AStream: TStream; DataStart, DataSize: LongWord);
  1504. var
  1505. Idx: TU4;
  1506. begin
  1507. Idx := 0;
  1508. with AStream do
  1509. begin
  1510. Read(FVMapType, 4);
  1511. ReadMotorolaNumber(AStream, @FDimensions, 2);
  1512. ReadS0(AStream, FName);
  1513. if FDimensions > 0 then
  1514. begin
  1515. while Cardinal(Position) < (DataStart + DataSize) do
  1516. begin
  1517. SetLength(FValues, Length(FValues) + 1);
  1518. ReadVXAsU2(AStream, @FValues[Idx].vert, 1);
  1519. SetLength(FValues[Idx].values, Dimensions * 4);
  1520. ReadMotorolaNumber(AStream, @FValues[Idx].values[0], 4, Dimensions);
  1521. Inc(Idx);
  1522. end;
  1523. end;
  1524. end;
  1525. end;
  1526. (*********************************** TLWTags **********************************)
  1527. destructor TLWTags.Destroy;
  1528. begin
  1529. inherited;
  1530. end;
  1531. procedure TLWTags.Clear;
  1532. begin
  1533. FreeAndNil(FTags);
  1534. end;
  1535. class function TLWTags.GetID: TID4;
  1536. begin
  1537. result := ID_TAGS;
  1538. end;
  1539. function TLWTags.GetTags: TStrings;
  1540. begin
  1541. if FTags = nil then
  1542. FTags := TStringList.Create;
  1543. result := FTags;
  1544. end;
  1545. procedure TLWTags.LoadData(AStream: TStream; DataStart, DataSize: LongWord);
  1546. var
  1547. EndPos: TU4;
  1548. TmpStr: string;
  1549. begin
  1550. EndPos := DataStart + DataSize;
  1551. while Cardinal(AStream.Position) < Cardinal(EndPos) do
  1552. begin
  1553. ReadS0(AStream, TmpStr);
  1554. Tags.Add(TmpStr);
  1555. TmpStr := '';
  1556. end;
  1557. end;
  1558. function TLWTags.TagToName(tag: TU2): string;
  1559. begin
  1560. result := Tags[tag];
  1561. end;
  1562. (********************************* TLWSubChunk ********************************)
  1563. procedure TLWSubChunk.LoadFromStream(AStream: TStream);
  1564. var
  1565. DataStart: Integer;
  1566. DataSize: TU2;
  1567. begin
  1568. with AStream do
  1569. begin
  1570. ReadMotorolaNumber(AStream, @DataSize, 2);
  1571. DataStart := Position;
  1572. FSize := DataSize;
  1573. LoadData(AStream, DataStart, DataSize);
  1574. Position := DataStart + DataSize + (DataStart + DataSize) mod 2;
  1575. end;
  1576. end;
  1577. (*********************************** TLWLayr **********************************)
  1578. destructor TLWLayr.Destroy;
  1579. begin
  1580. inherited;
  1581. end;
  1582. class function TLWLayr.GetID: TID4;
  1583. begin
  1584. result := ID_LAYR;
  1585. end;
  1586. procedure TLWLayr.LoadData(AStream: TStream; DataStart, DataSize: LongWord);
  1587. begin
  1588. ReadMotorolaNumber(AStream, @FNumber, 2);
  1589. ReadMotorolaNumber(AStream, @FFlags, 2);
  1590. ReadMotorolaNumber(AStream, @FPivot, 4, 3);
  1591. ReadS0(AStream, FName);
  1592. if ((DataStart + DataSize) - Cardinal(AStream.Position)) > 2 then
  1593. ReadMotorolaNumber(AStream, @FParent, 2);
  1594. end;
  1595. (*********************************** TLWSurf **********************************)
  1596. destructor TLWSurf.Destroy;
  1597. begin
  1598. inherited;
  1599. end;
  1600. class function TLWSurf.GetID: TID4;
  1601. begin
  1602. result := ID_SURF;
  1603. end;
  1604. function TLWSurf.GetParamAddr(Param: TID4): Pointer;
  1605. var
  1606. Idx: Integer;
  1607. sParam: string;
  1608. begin
  1609. result := inherited GetParamAddr(Param);
  1610. if (result = nil) and (Source <> '') then
  1611. begin
  1612. sParam := string(Param);
  1613. Idx := RootChunks.FindChunk(@FindSurfaceByName, @sParam, 0);
  1614. if Idx <> -1 then
  1615. result := TLWSurf(RootChunks[Idx]).ParamAddr[Param];
  1616. end;
  1617. end;
  1618. function TLWSurf.GetSurfId: Integer;
  1619. var
  1620. c, SurfIdx: Integer;
  1621. begin
  1622. c := 0;
  1623. SurfIdx := Owner.FindChunk(@FindChunkById, @ID_SURF);
  1624. while (SurfIdx <> -1) and (Owner[SurfIdx] <> Self) do
  1625. begin
  1626. SurfIdx := Owner.FindChunk(@FindChunkById, @ID_SURF, SurfIdx + 1);
  1627. Inc(c);
  1628. end;
  1629. result := c;
  1630. end;
  1631. procedure TLWSurf.LoadData(AStream: TStream; DataStart, DataSize: LongWord);
  1632. var
  1633. CurId: TID4;
  1634. begin
  1635. ReadS0(AStream, FName);
  1636. ReadS0(AStream, FSource);
  1637. while Cardinal(AStream.Position) < (DataStart + DataSize) do
  1638. begin
  1639. AStream.Read(CurId, 4);
  1640. Items.Add(GetChunkClass(CurId, TLWSubChunk).Create);
  1641. {$IFDEF WIN32}
  1642. with Items[Items.Count - 1] do
  1643. {$ELSE}
  1644. /// with Items[Items.Count - 1] do
  1645. {$ENDIF}
  1646. begin
  1647. FID := CurId;
  1648. LoadFromStream(AStream);
  1649. end;
  1650. end;
  1651. end;
  1652. (*********************************** TLWPTag **********************************)
  1653. constructor TLWPTag.Create;
  1654. begin
  1655. inherited;
  1656. end;
  1657. function TLWPTag.AddTag(Value: TU2): Integer;
  1658. var
  1659. i, L: Integer;
  1660. begin
  1661. result := -1;
  1662. L := Length(FTags);
  1663. for i := 0 to L - 1 do
  1664. if Value = FTags[i] then
  1665. begin
  1666. result := i;
  1667. Exit;
  1668. end;
  1669. if result = -1 then
  1670. begin
  1671. SetLength(FTags, L + 1);
  1672. FTags[L] := Value;
  1673. result := L;
  1674. end;
  1675. end;
  1676. procedure TLWPTag.Clear;
  1677. begin
  1678. SetLength(FTagMaps, 0);
  1679. SetLength(FTags, 0);
  1680. end;
  1681. function TLWPTag.GetPolsByTag(tag: TU2; var PolyIndices: TU2DynArray): Integer;
  1682. var
  1683. i: Integer;
  1684. procedure AddPoly(Value: TU2);
  1685. var
  1686. L: Integer;
  1687. begin
  1688. L := Length(PolyIndices);
  1689. SetLength(PolyIndices, L + 1);
  1690. PolyIndices[L] := Value;
  1691. end;
  1692. begin
  1693. for i := 0 to TagMapCount - 1 do
  1694. if TagMaps[i].tag = tag then
  1695. AddPoly(TagMaps[i].poly);
  1696. result := Length(PolyIndices);
  1697. end;
  1698. class function TLWPTag.GetID: TID4;
  1699. begin
  1700. result := ID_PTAG;
  1701. end;
  1702. function TLWPTag.GetTag(AIndex: Integer): TU2;
  1703. begin
  1704. ValidateTagInfo;
  1705. result := FTags[AIndex];
  1706. end;
  1707. function TLWPTag.GetTagCount: Integer;
  1708. begin
  1709. ValidateTagInfo;
  1710. result := Length(FTags);
  1711. end;
  1712. function TLWPTag.GetTagMapCount: Integer;
  1713. begin
  1714. result := Length(FTagMaps) div 2;
  1715. end;
  1716. function TLWPTag.GetTagMaps(AIndex: Integer): TLWPolyTagMap;
  1717. begin
  1718. result := PLWPolyTagMap(@FTagMaps[AIndex * 2])^;
  1719. end;
  1720. procedure TLWPTag.LoadData(AStream: TStream; DataStart, DataSize: LongWord);
  1721. var
  1722. Idx: Integer;
  1723. begin
  1724. Idx := 0;
  1725. with AStream do
  1726. begin
  1727. Read(FMapType, 4);
  1728. SetLength(FTagMaps, (DataSize - 4) div 2);
  1729. while Cardinal(Position) < (DataStart + DataSize) do
  1730. begin
  1731. ReadVXAsU2(AStream, @FTagMaps[Idx]);
  1732. ReadMotorolaNumber(AStream, @FTagMaps[Idx + 1], 2);
  1733. Inc(Idx, 2);
  1734. end;
  1735. // correct length guestimate errors if any
  1736. if (Idx + 1) < Length(FTagMaps) then
  1737. SetLength(FTagMaps, Idx + 1);
  1738. end;
  1739. end;
  1740. procedure TLWPTag.ValidateTagInfo;
  1741. var
  1742. i: Integer;
  1743. begin
  1744. if Length(FTags) > 0 then
  1745. Exit;
  1746. for i := 0 to TagMapCount - 1 do
  1747. AddTag(TagMaps[i].tag);
  1748. end;
  1749. (******************************** TLWParentChunk ******************************)
  1750. procedure TLWParentChunk.Clear;
  1751. begin
  1752. FreeAndNil(FItems);
  1753. inherited;
  1754. end;
  1755. function TLWParentChunk.GetFloatParam(Param: TID4): Single;
  1756. var
  1757. pdata: Pointer;
  1758. begin
  1759. pdata := ParamAddr[Param];
  1760. if pdata <> nil then
  1761. begin
  1762. result := PF4(pdata)^;
  1763. ReverseByteOrder(@result, 4);
  1764. end
  1765. else
  1766. result := 0.0;
  1767. end;
  1768. function TLWParentChunk.GetItems: TLWChunkList;
  1769. begin
  1770. if FItems = nil then
  1771. FItems := TLWChunkList.Create(true, Self);
  1772. result := FItems;
  1773. end;
  1774. function TLWParentChunk.GetLongParam(Param: TID4): LongWord;
  1775. var
  1776. pdata: Pointer;
  1777. begin
  1778. pdata := ParamAddr[Param];
  1779. if pdata <> nil then
  1780. begin
  1781. result := PU4(pdata)^;
  1782. ReverseByteOrder(@result, 4);
  1783. end
  1784. else
  1785. result := 0;
  1786. end;
  1787. function TLWParentChunk.GetParamAddr(Param: TID4): Pointer;
  1788. var
  1789. Idx: Integer;
  1790. begin
  1791. result := nil;
  1792. Idx := Items.FindChunk(@FindChunkById, @Param, 0);
  1793. if Idx <> -1 then
  1794. result := Items[Idx].data;
  1795. end;
  1796. function TLWPols.GetPolsByPntIdx(VertIdx: TU2;
  1797. var VertPolys: TU2DynArray): Integer;
  1798. var
  1799. i, j, L: Integer;
  1800. begin
  1801. L := 0;
  1802. if Length(VertPolys) > 0 then
  1803. SetLength(VertPolys, 0);
  1804. for i := 0 to PolsCount - 1 do
  1805. begin
  1806. for j := 1 to Indices[PolsByIndex[i]] do
  1807. begin
  1808. if Indices[PolsByIndex[i] + j] = VertIdx then
  1809. begin
  1810. L := Length(VertPolys);
  1811. SetLength(VertPolys, L + 1);
  1812. VertPolys[L] := i;
  1813. end;
  1814. end;
  1815. end;
  1816. result := L;
  1817. end;
  1818. function TLWChunkList.Add(AChunk: TLWChunk): Integer;
  1819. begin
  1820. if (FOwner <> nil) and (FOwner is TLWParentChunk) then
  1821. AChunk.FParentChunk := TLWParentChunk(FOwner);
  1822. AChunk.FOwner := Self;
  1823. result := inherited Add(AChunk);
  1824. end;
  1825. procedure TLWPols.CalcPntsNormals;
  1826. var
  1827. i, j, k, PntIdx, PolyIdx, SurfIdx: Integer;
  1828. Pnts: TLWPnts;
  1829. // PTags: TLWPTag;
  1830. TmpAddr: Pointer;
  1831. sman: TF4;
  1832. begin
  1833. // Todo: CalcPntsNormals
  1834. if IndiceCount = 0 then
  1835. Exit;
  1836. with ParentChunk as TLWLayr do
  1837. Pnts := TLWPnts(Items[Items.FindChunk(@FindChunkById, @ID_PNTS, 0)]);
  1838. for PolyIdx := 0 to PolsCount - 1 do
  1839. begin
  1840. i := PolsByIndex[PolyIdx];
  1841. SurfIdx := RootChunks.FindChunk(@FindSurfaceByTag,
  1842. @FPolsInfo[PolyIdx].surfid);
  1843. TmpAddr := TLWSurf(RootChunks[SurfIdx]).ParamAddr[ID_SMAN];
  1844. if TmpAddr <> nil then
  1845. begin
  1846. sman := PF4(TmpAddr)^;
  1847. ReverseByteOrder(@sman, 4);
  1848. end
  1849. else
  1850. sman := 0;
  1851. for j := 1 to Indices[i] do
  1852. begin
  1853. FPolsInfo[PolyIdx].vnorms[j - 1] := FPolsInfo[PolyIdx].norm;
  1854. if sman <= 0 then
  1855. continue;
  1856. PntIdx := Indices[i + j];
  1857. for k := 0 to Pnts.PntsInfo[PntIdx].npols - 1 do
  1858. begin
  1859. if Pnts.PntsInfo[PntIdx].pols[k] = PolyIdx then
  1860. continue;
  1861. if ArcCos(VecDot(FPolsInfo[PolyIdx].norm,
  1862. FPolsInfo[Pnts.PntsInfo[PntIdx].pols[k]].norm)) > sman then
  1863. continue;
  1864. FPolsInfo[PolyIdx].vnorms[j - 1] :=
  1865. VecAdd(FPolsInfo[PolyIdx].vnorms[j - 1],
  1866. FPolsInfo[Pnts.PntsInfo[PntIdx].pols[k]].norm);
  1867. end;
  1868. FPolsInfo[PolyIdx].vnorms[j - 1] :=
  1869. VecNorm(FPolsInfo[PolyIdx].vnorms[j - 1]);
  1870. end;
  1871. end;
  1872. end;
  1873. function TLWChunk.GetRootChunks: TLWChunkList;
  1874. var
  1875. Parent: TLWParentChunk;
  1876. begin
  1877. result := nil;
  1878. if (FParentChunk = nil) then
  1879. begin
  1880. if (FOwner is TLWChunkList) then
  1881. begin
  1882. result := FOwner;
  1883. Exit;
  1884. end;
  1885. end
  1886. else
  1887. begin
  1888. Parent := FParentChunk;
  1889. while not(Parent.ParentChunk = nil) do
  1890. Parent := Parent.ParentChunk;
  1891. result := Parent.Owner;
  1892. end;
  1893. end;
  1894. function TLWChunkList.FindChunk(ChunkFind: TLWChunkFind; Criteria: Pointer;
  1895. StartIndex: Integer): Integer;
  1896. var
  1897. Found: boolean;
  1898. begin
  1899. Found := false;
  1900. result := -1;
  1901. while (StartIndex < Count) and (not Found) do
  1902. begin
  1903. ChunkFind(Items[StartIndex], Criteria, Found);
  1904. if Found then
  1905. begin
  1906. result := StartIndex;
  1907. Exit;
  1908. end;
  1909. Inc(StartIndex);
  1910. end;
  1911. end;
  1912. function TLWChunk.GetIndex: Integer;
  1913. begin
  1914. result := Owner.IndexOf(Self);
  1915. end;
  1916. procedure TLWChunk.Loaded;
  1917. begin
  1918. // do nothing
  1919. end;
  1920. procedure TLWChunkList.Loaded;
  1921. var
  1922. i: Integer;
  1923. begin
  1924. for i := 0 to Count - 1 do
  1925. begin
  1926. Items[i].Loaded;
  1927. end;
  1928. end;
  1929. function TLWParentChunk.GetVec3Param(Param: TID4): TVec12;
  1930. var
  1931. pdata: Pointer;
  1932. begin
  1933. pdata := ParamAddr[Param];
  1934. if pdata <> nil then
  1935. begin
  1936. result := PVec12(pdata)^;
  1937. ReverseByteOrder(@result, 4, 3);
  1938. end
  1939. else
  1940. begin
  1941. result[0] := 0;
  1942. result[1] := 1;
  1943. result[2] := 2;
  1944. end;
  1945. end;
  1946. function TLWParentChunk.GetVXParam(Param: TID4): Word;
  1947. var
  1948. pdata: Pointer;
  1949. begin
  1950. pdata := ParamAddr[Param];
  1951. if pdata <> nil then
  1952. result := ValueOfVX(pdata)
  1953. else
  1954. result := 0;
  1955. end;
  1956. function TLWParentChunk.GetWordParam(Param: TID4): Word;
  1957. var
  1958. pdata: Pointer;
  1959. begin
  1960. pdata := ParamAddr[Param];
  1961. if pdata <> nil then
  1962. begin
  1963. result := PU4(pdata)^;
  1964. ReverseByteOrder(@result, 2);
  1965. end
  1966. else
  1967. result := 0;
  1968. end;
  1969. procedure TLWParentChunk.Loaded;
  1970. begin
  1971. Items.Loaded;
  1972. end;
  1973. procedure TLWPols.Loaded;
  1974. begin
  1975. inherited;
  1976. CalcPntsNormals;
  1977. end;
  1978. function TLWObjectFile.TagToName(tag: TU2): string;
  1979. var
  1980. TagsIdx: Integer;
  1981. begin
  1982. TagsIdx := Chunks.FindChunk(@FindChunkById, @ID_TAGS);
  1983. if TagsIdx <> -1 then
  1984. result := TLWTags(Chunks[TagsIdx]).TagToName(tag);
  1985. end;
  1986. (******************** TLWClip ********************)
  1987. class function TLWClip.GetID: TID4;
  1988. begin
  1989. result := ID_CLIP;
  1990. end;
  1991. procedure TLWClip.LoadData(AStream: TStream; DataStart, DataSize: LongWord);
  1992. var
  1993. CurId: TID4;
  1994. begin
  1995. ReadMotorolaNumber(AStream, @FClipIndex, 4);
  1996. while Cardinal(AStream.Position) < (DataStart + DataSize) do
  1997. begin
  1998. AStream.Read(CurId, 4);
  1999. Items.Add(GetChunkClass(CurId, TLWSubChunk).Create);
  2000. {$IFDEF WIN32}
  2001. with Items[Items.Count - 1] do
  2002. {$ELSE}
  2003. /// with Items[Items.Count - 1] do
  2004. {$ENDIF}
  2005. begin
  2006. FID := CurId;
  2007. LoadFromStream(AStream);
  2008. end;
  2009. end;
  2010. end;
  2011. // TLWContentDir
  2012. (* function TLWContentDir.ContentSearch(AFilename: string): string;
  2013. var
  2014. i: Integer;
  2015. begin
  2016. if not FileExists(AFilename) then
  2017. begin
  2018. result := ExtractFileName(AFilename);
  2019. if not FileExists(result) then
  2020. begin
  2021. for i := 0 to SubDirs.Count - 1 do
  2022. begin
  2023. if FileExists(Root+'\'+SubDirs[i]+'\'+result) then
  2024. begin
  2025. result:=Root+'\'+SubDirs[i]+'\'+result;
  2026. Exit;
  2027. end;
  2028. end;
  2029. result := '';
  2030. end;
  2031. end;
  2032. end;
  2033. *)
  2034. destructor TLWContentDir.Destroy;
  2035. begin
  2036. FreeAndNil(FSubDirs);
  2037. inherited;
  2038. end;
  2039. function TLWContentDir.FindContent(AFilename: string): string;
  2040. var
  2041. i: Integer;
  2042. begin
  2043. if not FileExists(AFilename) then
  2044. begin
  2045. result := ExtractFileName(AFilename);
  2046. if not FileExists(result) then
  2047. begin
  2048. for i := 0 to SubDirs.Count - 1 do
  2049. begin
  2050. if FileExists(Root + '\' + SubDirs[i] + '\' + result) then
  2051. begin
  2052. result := Root + '\' + SubDirs[i] + '\' + result;
  2053. Exit;
  2054. end;
  2055. end;
  2056. result := '';
  2057. end;
  2058. end;
  2059. end;
  2060. function TLWContentDir.GetSubDirs: TStrings;
  2061. begin
  2062. if FSubDirs = nil then
  2063. FSubDirs := TStringList.Create;
  2064. result := FSubDirs;
  2065. end;
  2066. procedure TLWContentDir.SetRoot(const Value: string);
  2067. begin
  2068. FRoot := Value;
  2069. end;
  2070. procedure TLWContentDir.SetSubDirs(const Value: TStrings);
  2071. begin
  2072. SubDirs.Assign(Value);
  2073. end;
  2074. //--------------------------------------------------------------------------
  2075. initialization
  2076. //--------------------------------------------------------------------------
  2077. // Pnts
  2078. RegisterChunkClass(TLWPnts);
  2079. // Pols
  2080. RegisterChunkClass(TLWPols);
  2081. // VMap
  2082. RegisterChunkClass(TLWVMap);
  2083. // Tags
  2084. RegisterChunkClass(TLWTags);
  2085. // PTAG
  2086. RegisterChunkClass(TLWPTag);
  2087. // SURF
  2088. RegisterChunkClass(TLWSurf);
  2089. // LAYR
  2090. RegisterChunkClass(TLWLayr);
  2091. // CLIP
  2092. RegisterChunkClass(TLWClip);
  2093. finalization
  2094. // UnRegisterChunkClasses;
  2095. FreeAndNil(ChunkClasses);
  2096. FreeAndNil(ContentDir);
  2097. end.