Formats.LWO.pas 59 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469
  1. //
  2. // The multimedia graphics platform 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. {$I GLScene.inc}
  160. uses
  161. System.Classes,
  162. System.SysUtils,
  163. System.IOUtils,
  164. System.Math,
  165. GLS.VectorGeometry;
  166. type
  167. TID4 = array [0 .. 3] of AnsiChar;
  168. PID4 = ^TID4;
  169. TID4DynArray = array of TID4;
  170. const
  171. ID_NULL = '#0#0#0#0'; // NULL ID
  172. ID_LWSC: TID4 = 'LWSC'; // Lightwave scene file
  173. ID_FORM: TID4 = 'FORM'; // IFF Form
  174. ID_LWOB: TID4 = 'LWOB'; // Lightwave Object version 1.0 - 5.x
  175. ID_LWLO: TID4 = 'LWLO'; // Lightwave Layered Object
  176. ID_LAYR: TID4 = 'LAYR'; // LAYER
  177. ID_PNTS: TID4 = 'PNTS'; // Points chunk
  178. ID_SRFS: TID4 = 'SRFS'; // Surface Names chunk
  179. ID_POLS: TID4 = 'POLS'; // Polygons chunk
  180. ID_CRVS: TID4 = 'CRVS'; // Curves chunk
  181. ID_PCHS: TID4 = 'PCHS'; // Patches chunk
  182. ID_SURF: TID4 = 'SURF'; // Surfaces chunk
  183. ID_COLR: TID4 = 'COLR'; // Color chunk
  184. ID_FLAG: TID4 = 'FLAG'; // Surface Flags
  185. ID_LUMI: TID4 = 'LUMI'; // Luminosity
  186. ID_DIFF: TID4 = 'DIFF'; // Diffuse
  187. ID_SPEC: TID4 = 'SPEC'; // Specular
  188. ID_REFL: TID4 = 'REFL'; // Reflective
  189. ID_TRAN: TID4 = 'TRAN'; // Transparency
  190. ID_VLUM: TID4 = 'VLUM'; // Luminosity
  191. ID_VDIF: TID4 = 'VDIF'; // Diffuse
  192. ID_VSPC: TID4 = 'VSPC'; // Specularity
  193. ID_VRFL: TID4 = 'VRFL'; // Reflective
  194. ID_VTRN: TID4 = 'VTRN'; // Transparency
  195. ID_GLOS: TID4 = 'GLOS'; // Glossiness SmallInt
  196. ID_SIDE: TID4 = 'SIDE'; // Sidedness
  197. ID_RFLT: TID4 = 'RFLT'; // REFLECTION MODE (PRE 6.0)
  198. ID_RFOP: TID4 = 'RFOP'; // REFLECTION OPTIONS
  199. ID_RIMG: TID4 = 'RIMG'; // REFLECTION IMAGE
  200. ID_RSAN: TID4 = 'RSAN'; // REFLECTION MAP SEAM ANGLE
  201. ID_RIND: TID4 = 'RIND'; // REFRACTIVE INDEX
  202. ID_EDGE: TID4 = 'EDGE'; // EDGE TRANSPARENCY THRESHOLD
  203. ID_SMAN: TID4 = 'SMAN'; // SMOOTHING ANGLE RADIANS
  204. ID_ALPH: TID4 = 'ALPH'; // ALPHA MODE
  205. ID_CTEX: TID4 = 'CTEX'; // COLOR TEXTURE
  206. ID_DTEX: TID4 = 'DTEX'; // DIFFUSE TEXTURE
  207. ID_STEX: TID4 = 'STEX'; // SPECULAR TEXTURE
  208. ID_RTEX: TID4 = 'RTEX'; // REFLECTIION TEXTURE
  209. ID_TTEX: TID4 = 'TTEX'; // TRANSPARENCY TEXTURE
  210. ID_LTEX: TID4 = 'LTEX'; // LUMINANCE TEXTURE
  211. ID_BTEX: TID4 = 'BTEX'; // BUMP TEXTURE
  212. ID_TFLG: TID4 = 'TFLG'; // TEXTURE FLAGS
  213. ID_TSIZ: TID4 = 'TSIZ'; // TEXTURE SIZE
  214. ID_TCTR: TID4 = 'TCTR'; // TEXTURE CENTER
  215. ID_TFAL: TID4 = 'TFAL'; // TEXTURE FALLOFF
  216. ID_TVEL: TID4 = 'TVAL'; // TEXTURE VALUE
  217. ID_TREF: TID4 = 'TREF'; // TEXTURE REFERENCE
  218. ID_TCLR: TID4 = 'TCLR'; // TEXTURE COLOR
  219. ID_TVAL: TID4 = 'TVAL'; // TEXTURE VALUE
  220. ID_TAMP: TID4 = 'TAMP'; // TEXTURE AMPLITUDE
  221. ID_TFP0: TID4 = 'TFP0'; // TEXTURE PARAMETERS
  222. ID_TFP1: TID4 = 'TFP1'; //
  223. ID_TFP2: TID4 = 'TFP2'; //
  224. ID_TIP0: TID4 = 'TIP0'; //
  225. ID_TIP1: TID4 = 'TIP1'; //
  226. ID_TIP2: TID4 = 'TIP2'; //
  227. ID_TSP0: TID4 = 'TSP0'; //
  228. ID_TSP1: TID4 = 'TSP1'; //
  229. ID_TSP2: TID4 = 'TSP2'; //
  230. ID_TFRQ: TID4 = 'TFRQ'; //
  231. ID_TIMG: TID4 = 'TIMG'; // TEXTURE IMG
  232. ID_TALP: TID4 = 'TALP'; //
  233. ID_TWRP: TID4 = 'TWRP'; // TEXTURE WRAP
  234. ID_TAAS: TID4 = 'TAAS'; //
  235. ID_TOPC: TID4 = 'TOPC'; //
  236. ID_SHDR: TID4 = 'SHDR'; //
  237. ID_SDAT: TID4 = 'SDAT'; //
  238. ID_IMSQ: TID4 = 'IMSQ'; // IMAGE SEQUENCE
  239. ID_FLYR: TID4 = 'FLYR'; // FLYER SEQUENCE
  240. ID_IMCC: TID4 = 'IMCC'; //
  241. SURF_FLAG_LUMINOUS = 1;
  242. SURF_FLAG_OUTLINE = 2;
  243. SURF_FLAG_SMOOTHING = 4;
  244. SURF_FLAG_COLORHIGHLIGHTS = 8;
  245. SURF_FLAG_COLORFILTER = 16;
  246. SURF_FLAG_OPAQUEEDGE = 32;
  247. SURF_FLAG_TRANSPARENTEDGE = 64;
  248. SURF_FLAG_SHARPTERMINATOR = 128;
  249. SURF_FLAG_DOUBLESIDED = 256;
  250. SURF_FLAG_ADDITIVE = 512;
  251. SURF_FLAG_SHADOWALPHA = 1024;
  252. CURV_CONTINUITY_FIRST = 1;
  253. CURV_CONTINUITY_LAST = 2;
  254. IMSQ_FLAG_LOOP = 1;
  255. IMSQ_FLAG_INTERLACE = 2;
  256. ID_LWO2: TID4 = 'LWO2'; // OBJECT
  257. ID_VMAP: TID4 = 'VMAP'; // VERTEX MAP
  258. ID_TAGS: TID4 = 'TAGS'; // TAGS?
  259. ID_PTAG: TID4 = 'PTAG'; // POLYGON TAG MAP
  260. ID_VMAD: TID4 = 'VMAD'; // DISCONTINUOUS VERTEX MAP
  261. ID_ENVL: TID4 = 'ENVL'; // ENVELOPE
  262. ID_CLIP: TID4 = 'CLIP'; // CLIP
  263. ID_BBOX: TID4 = 'BBOX'; // BOUNDING BOX
  264. ID_DESC: TID4 = 'DESC'; // DESCRIPTION
  265. ID_TEXT: TID4 = 'TEXT'; // TEXT
  266. ID_ICON: TID4 = 'ICON'; // ICON
  267. ENVL_PRE: TID4 = 'PRE'#0; // PRE-BEHAVIOUR
  268. ENVL_POST: TID4 = 'POST'; // POST
  269. ENVL_KEY: TID4 = 'KEY'#0; // KEY
  270. ENVL_SPAN: TID4 = 'SPAN'; // SPAN
  271. ENVL_CHAN: TID4 = 'CHAN'; // CHAN
  272. ENVL_NAME: TID4 = 'NAME'; // NAME
  273. ID_STIL: TID4 = 'STIL'; // STILL IMAGE FILENAME
  274. ID_ISEQ: TID4 = 'ISEQ'; // IMAGE SEQUENCE
  275. ID_ANIM: TID4 = 'ANIM'; // PLUGIN ANIMATION
  276. ID_STCC: TID4 = 'STCC'; // COLOR CYCLING STILL
  277. ID_CONT: TID4 = 'CONT'; // CONTRAST
  278. ID_BRIT: TID4 = 'BRIT'; // BRIGHTNESS
  279. ID_SATR: TID4 = 'SATR'; // SATURATION
  280. ID_HUE: TID4 = 'HUE'#0; // HUE
  281. ID_GAMMA: TID4 = 'GAMM'; // GAMMA
  282. ID_NEGA: TID4 = 'NEGA'; // NEGATIVE IMAGE
  283. ID_IFLT: TID4 = 'IFLT'; // IMAGE PLUG-IN FILTER
  284. ID_PFLT: TID4 = 'PFLT'; // PIXEL PLUG-IN FILTER
  285. POLS_TYPE_FACE: TID4 = 'FACE'; // FACES
  286. POLS_TYPE_CURV: TID4 = 'CURV'; // CURVE
  287. POLS_TYPE_PTCH: TID4 = 'PTCH'; // PATCH
  288. POLS_TYPE_MBAL: TID4 = 'MBAL'; // METABALL
  289. POLS_TYPE_BONE: TID4 = 'BONE'; // SKELEGON?
  290. VMAP_TYPE_PICK: TID4 = 'PICK'; // SELECTION SET
  291. VMAP_TYPE_WGHT: TID4 = 'WGHT'; // WEIGHT MAP
  292. VMAP_TYPE_MNVW: TID4 = 'MNVW'; // SUBPATCH WEIGHT MAP
  293. VMAP_TYPE_TXUV: TID4 = 'TXUV'; // UV MAP
  294. VMAP_TYPE_RGB: TID4 = 'RGB'#0; // RGB MAP
  295. VMAP_TYPE_RGBA: TID4 = 'RGBA'; // RGBA MAP
  296. VMAP_TYPE_MORF: TID4 = 'MORF'; // MORPH MAP: RELATIVE VERTEX DISPLACEMENT
  297. VMAP_TYPE_SPOT: TID4 = 'SPOT'; // SPOT MAP: ABSOLUTE VERTEX POSITIONS
  298. PTAG_TYPE_SURF: TID4 = 'SURF'; // SURFACE
  299. PTAG_TYPE_PART: TID4 = 'PART'; // PARENT PART
  300. PTAG_TYPE_SMGP: TID4 = 'SMGP'; // SMOOTH GROUP
  301. PRE_POST_RESET = 0; // RESET
  302. PRE_POST_CONSTANT = 1; // CONSTANT
  303. PRE_POST_REPEAT = 2; // REPEAT
  304. PRE_POST_OSCILLATE = 3; // OSCILLATE
  305. PRE_POST_OFFSET = 4; // OFFSET REPEAT
  306. PRE_POST_LINEAR = 5; // LINEAR
  307. POLS_VCOUNT_MASK = $3FF;
  308. POLS_FLAGS_MASK = $FC00;
  309. SIDE_FRONT = 1;
  310. SIDE_BACK = 2;
  311. SIDE_FRONT_AND_BACK = SIDE_FRONT and SIDE_BACK;
  312. RFOP_BACKDROP = 0;
  313. RFOP_RAYTRACEANDBACKDROP = 1;
  314. RFOP_SPHERICALMAP = 2;
  315. RFOP_RAYTRACEANDSPHERICALMAP = 3;
  316. type
  317. TI1 = ShortInt;
  318. PI1 = ^TI1;
  319. TI2 = SmallInt;
  320. PI2 = ^TI2;
  321. TI4 = LongInt;
  322. PI4 = ^TI4;
  323. TU1 = Byte;
  324. PU1 = ^TU1;
  325. TU1DynArray = array of TU1;
  326. TU2 = Word;
  327. PU2 = ^TU2;
  328. TU2Array = array [0 .. 65534] of TU2;
  329. PU2Array = ^TU2Array;
  330. TU2DynArray = array of TU2;
  331. TU4 = LongWord;
  332. PU4 = ^TU4;
  333. TU4Array = array [0 .. 65534] of TU4;
  334. PU4Array = ^TU4Array;
  335. TU4DynArray = array of TU4;
  336. TF4 = Single;
  337. PF4 = ^TF4;
  338. TF4Array = array [0 .. 65534] of TF4;
  339. PF4Array = ^TF4Array;
  340. TF4DynArray = array of TF4;
  341. TANG4 = TF4;
  342. PANG4 = ^TANG4;
  343. // TS0 = PAnsiChar;
  344. TVec12 = array [0 .. 2] of TF4;
  345. PVec12 = ^TVec12;
  346. TVec12Array = array [0 .. 65534] of TVec12;
  347. PVec12Array = ^TVec12Array;
  348. TVec12DynArray = array of TVec12;
  349. TColr12 = TVec12;
  350. PColr12 = ^TColr12;
  351. TColr12DynArray = array of TColr12;
  352. TColr4 = array [0 .. 3] of TU1;
  353. PColr4 = ^TColr4;
  354. // Lightwave Chunk Struct - Used in TLWOReadCallback
  355. PLWChunkRec = ^TLWChunkRec;
  356. TLWChunkRec = record
  357. id: TID4;
  358. size: TU4;
  359. data: Pointer;
  360. end;
  361. // Lightwave SubChunk Struct - Used in TLWOReadCallback
  362. PLWSubChunkRec = ^TLWSubChunkRec;
  363. TLWSubChunkRec = record
  364. id: TID4;
  365. size: TU2;
  366. data: Pointer;
  367. end;
  368. TLWPolsInfo = record
  369. norm: TVec12;
  370. vnorms: TVec12DynArray;
  371. surfid: TU2;
  372. end;
  373. TLWPolsInfoDynArray = array of TLWPolsInfo;
  374. TLWPntsInfo = record
  375. npols: TU2;
  376. pols: TU2DynArray;
  377. end;
  378. TLWPntsInfoDynArray = array of TLWPntsInfo;
  379. TLWPolsDynArray = TU2DynArray;
  380. TLWPolyTagMapDynArray = TU2DynArray;
  381. TLWPolyTagMap = record
  382. poly: TU2;
  383. tag: TU2;
  384. end;
  385. PLWPolyTagMap = ^TLWPolyTagMap;
  386. // Value Map
  387. TLWVertexMap = record
  388. vert: TU2;
  389. values: TF4DynArray;
  390. end;
  391. TLWVertexMapDynArray = array of TLWVertexMap;
  392. TLWChunkList = class;
  393. TLWParentChunk = class;
  394. TLWChunk = class(TPersistent)
  395. private
  396. FData: Pointer;
  397. FID: TID4;
  398. FSize: TU4;
  399. FParentChunk: TLWParentChunk;
  400. FOwner: TLWChunkList;
  401. function GetRootChunks: TLWChunkList;
  402. function GetIndex: Integer;
  403. protected
  404. procedure Clear; virtual;
  405. procedure LoadData(AStream: TStream;
  406. DataStart, DataSize: LongWord); virtual;
  407. procedure Loaded; virtual;
  408. public
  409. destructor Destroy; override;
  410. class function GetID: TID4; virtual;
  411. procedure LoadFromStream(AStream: TStream); virtual;
  412. property data: Pointer read FData;
  413. property id: TID4 read FID;
  414. property size: TU4 read FSize;
  415. // ParentChunk may be nil indicating this is a root chunk. ie. TLWLayr
  416. property ParentChunk: TLWParentChunk read FParentChunk;
  417. property RootChunks: TLWChunkList read GetRootChunks;
  418. property Index: Integer read GetIndex;
  419. property Owner: TLWChunkList read FOwner;
  420. end;
  421. TLWChunkClass = class of TLWChunk;
  422. TLWSubChunk = class(TLWChunk)
  423. public
  424. procedure LoadFromStream(AStream: TStream); override;
  425. end;
  426. TLWChunkFind = procedure(AChunk: TLWChunk; Criteria: Pointer;
  427. var Found: boolean);
  428. TLWChunkList = class(TList)
  429. private
  430. FOwnsItems: boolean;
  431. FOwner: TObject;
  432. function GetItem(Index: Integer): TLWChunk;
  433. protected
  434. procedure Loaded; virtual;
  435. public
  436. constructor Create(AOwnsItems: boolean; AOwner: TObject);
  437. destructor Destroy; override;
  438. function Add(AChunk: TLWChunk): Integer;
  439. procedure Clear; override;
  440. procedure Delete(Index: Integer);
  441. function FindChunk(ChunkFind: TLWChunkFind; Criteria: Pointer;
  442. StartIndex: Integer = 0): Integer;
  443. property Items[Index: Integer]: TLWChunk read GetItem; default;
  444. property OwnsItems: boolean read FOwnsItems;
  445. property Owner: TObject read FOwner;
  446. end;
  447. TLWParentChunk = class(TLWChunk)
  448. private
  449. FItems: TLWChunkList;
  450. function GetItems: TLWChunkList;
  451. function GetFloatParam(Param: TID4): Single;
  452. function GetWordParam(Param: TID4): Word;
  453. function GetVec3Param(Param: TID4): TVec12;
  454. function GetLongParam(Param: TID4): LongWord;
  455. function GetVXParam(Param: TID4): Word;
  456. protected
  457. function GetParamAddr(Param: TID4): Pointer; virtual;
  458. procedure Clear; override;
  459. procedure Loaded; override;
  460. public
  461. property Items: TLWChunkList read GetItems;
  462. property ParamAddr[Param: TID4]: Pointer read GetParamAddr;
  463. property FloatParam[Param: TID4]: Single read GetFloatParam;
  464. property WordParam[Param: TID4]: Word read GetWordParam;
  465. property LongParam[Param: TID4]: LongWord read GetLongParam;
  466. property Vec3Param[Param: TID4]: TVec12 read GetVec3Param;
  467. property VXParam[Param: TID4]: Word read GetVXParam;
  468. end;
  469. TLWVMap = class;
  470. TLWPnts = class(TLWParentChunk)
  471. private
  472. FPnts: TVec12DynArray;
  473. FPntsInfo: TLWPntsInfoDynArray;
  474. function GetPntsCount: LongWord;
  475. function AddPoly(PntIdx, PolyIdx: Integer): Integer;
  476. protected
  477. procedure Clear; override;
  478. procedure LoadData(AStream: TStream;
  479. DataStart, DataSize: LongWord); override;
  480. public
  481. class function GetID: TID4; override;
  482. function GetVMap(VMapID: TID4; out VMap: TLWVMap): boolean;
  483. property PntsCount: LongWord read GetPntsCount;
  484. property Pnts: TVec12DynArray read FPnts;
  485. property PntsInfo: TLWPntsInfoDynArray read FPntsInfo;
  486. end;
  487. TLWPols = class(TLWParentChunk)
  488. private
  489. FPolsType: TID4;
  490. FPols: TLWPolsDynArray;
  491. FPolsInfo: TLWPolsInfoDynArray;
  492. FPolsCount: Integer;
  493. function GetPolsByIndex(AIndex: TU2): Integer;
  494. function GetIndiceCount: TU4;
  495. function GetIndice(AIndex: Integer): TU2;
  496. function GetPolsCount: Integer;
  497. procedure CalcPolsNormals;
  498. procedure CalcPntsNormals;
  499. protected
  500. procedure Clear; override;
  501. procedure LoadData(AStream: TStream;
  502. DataStart, DataSize: LongWord); override;
  503. procedure Loaded; override;
  504. public
  505. class function GetID: TID4; override;
  506. function GetPolsByPntIdx(VertIdx: TU2; var VertPolys: TU2DynArray): Integer;
  507. property PolsByIndex[AIndex: TU2]: Integer read GetPolsByIndex;
  508. property IndiceCount: TU4 read GetIndiceCount;
  509. property Indices[AIndex: Integer]: TU2 read GetIndice;
  510. property PolsType: TID4 read FPolsType;
  511. property PolsCount: Integer read GetPolsCount;
  512. property PolsInfo: TLWPolsInfoDynArray read FPolsInfo;
  513. end;
  514. TLWVMap = class(TLWChunk)
  515. private
  516. FDimensions: TU2;
  517. FName: string;
  518. FValues: TLWVertexMapDynArray;
  519. FVMapType: TID4;
  520. function GetValue(AIndex: TU2): TLWVertexMap;
  521. function GetValueCount: Integer;
  522. protected
  523. procedure Clear; override;
  524. procedure LoadData(AStream: TStream;
  525. DataStart, DataSize: LongWord); override;
  526. public
  527. class function GetID: TID4; override;
  528. property Dimensions: TU2 read FDimensions;
  529. property Name: string read FName;
  530. property Value[AIndex: TU2]: TLWVertexMap read GetValue;
  531. property ValueCount: Integer read GetValueCount;
  532. property VMapType: TID4 read FVMapType;
  533. end;
  534. TLWTags = class(TLWChunk)
  535. private
  536. FTags: TStrings;
  537. function GetTags: TStrings;
  538. protected
  539. procedure Clear; override;
  540. procedure LoadData(AStream: TStream;
  541. DataStart, DataSize: LongWord); override;
  542. public
  543. destructor Destroy; override;
  544. class function GetID: TID4; override;
  545. function TagToName(tag: TU2): string;
  546. property Tags: TStrings read GetTags;
  547. end;
  548. TLWSurf = class(TLWParentChunk)
  549. private
  550. FName: string;
  551. FSource: string;
  552. function GetSurfId: Integer;
  553. protected
  554. function GetParamAddr(Param: TID4): Pointer; override;
  555. procedure LoadData(AStream: TStream;
  556. DataStart, DataSize: LongWord); override;
  557. public
  558. destructor Destroy; override;
  559. class function GetID: TID4; override;
  560. property surfid: Integer read GetSurfId;
  561. property Name: string read FName;
  562. property Source: string read FSource;
  563. end;
  564. TLWLayr = class(TLWParentChunk)
  565. private
  566. FFlags: TU2;
  567. FName: string;
  568. FNumber: TU2;
  569. FParent: TU2;
  570. FPivot: TVec12;
  571. protected
  572. procedure LoadData(AStream: TStream;
  573. DataStart, DataSize: LongWord); override;
  574. public
  575. destructor Destroy; override;
  576. class function GetID: TID4; override;
  577. property Flags: TU2 read FFlags;
  578. property Name: string read FName;
  579. property Number: TU2 read FNumber;
  580. property Parent: TU2 read FParent;
  581. property Pivot: TVec12 read FPivot;
  582. end;
  583. TLWPTag = class(TLWChunk)
  584. private
  585. FMapType: TID4;
  586. FTagMaps: TLWPolyTagMapDynArray;
  587. FTags: TU2DynArray;
  588. function AddTag(Value: TU2): Integer;
  589. function GetTag(AIndex: Integer): TU2;
  590. function GetTagCount: Integer;
  591. function GetTagMapCount: Integer;
  592. function GetTagMaps(AIndex: Integer): TLWPolyTagMap;
  593. procedure ValidateTagInfo;
  594. protected
  595. procedure Clear; override;
  596. procedure LoadData(AStream: TStream;
  597. DataStart, DataSize: LongWord); override;
  598. public
  599. constructor Create;
  600. function GetPolsByTag(tag: TU2; var PolyIndices: TU2DynArray): Integer;
  601. class function GetID: TID4; override;
  602. property MapType: TID4 read FMapType;
  603. property TagCount: Integer read GetTagCount;
  604. property TagMapCount: Integer read GetTagMapCount;
  605. property TagMaps[AIndex: Integer]: TLWPolyTagMap read GetTagMaps; default;
  606. property Tags[AIndex: Integer]: TU2 read GetTag;
  607. end;
  608. TLWObjectFile = class(TObject)
  609. private
  610. FChunks: TLWChunkList;
  611. FFileName: string;
  612. function GetChunks: TLWChunkList;
  613. function GetCount: Integer;
  614. function GetSurfaceByName(Index: string): TLWSurf;
  615. function GetSurfaceByTag(Index: TU2): TLWSurf;
  616. public
  617. constructor Create;
  618. destructor Destroy; override;
  619. function TagToName(tag: TU2): string;
  620. procedure LoadFromFile(const AFilename: string);
  621. procedure LoadFromStream(AStream: TStream);
  622. property ChunkCount: Integer read GetCount;
  623. property Chunks: TLWChunkList read GetChunks;
  624. property FileName: string read FFileName;
  625. property SurfaceByName[Index: string]: TLWSurf read GetSurfaceByName;
  626. property SurfaceByTag[Index: TU2]: TLWSurf read GetSurfaceByTag;
  627. end;
  628. TLWClip = class(TLWParentChunk)
  629. private
  630. FClipIndex: TU4;
  631. protected
  632. procedure LoadData(AStream: TStream;
  633. DataStart, DataSize: LongWord); override;
  634. public
  635. class function GetID: TID4; override;
  636. property ClipIndex: TU4 read FClipIndex;
  637. end;
  638. TLWContentNotify = procedure(Sender: TObject; var Content: string) of object;
  639. TLWContentDir = class
  640. private
  641. FSubDirs: TStrings;
  642. FRoot: string;
  643. function GetSubDirs: TStrings;
  644. procedure SetRoot(const Value: string);
  645. procedure SetSubDirs(const Value: TStrings);
  646. // function ContentSearch(AFilename: string): string;
  647. public
  648. destructor Destroy; override;
  649. function FindContent(AFilename: string): string;
  650. property Root: string read FRoot write SetRoot;
  651. property SubDirs: TStrings read GetSubDirs write SetSubDirs;
  652. end;
  653. TLWOReadCallback = procedure(Chunk: TLWChunkRec; data: Pointer); cdecl;
  654. procedure RegisterChunkClass(ChunkClass: TLWChunkClass);
  655. function LoadLW0FromStream(Stream: TStream; ReadCallback: TLWOReadCallback;
  656. UserData: Pointer): LongWord; cdecl;
  657. function LoadLWOFromFile(const AFilename: string;
  658. ReadCallback: TLWOReadCallback; UserData: Pointer): LongWord;
  659. procedure ReadMotorolaNumber(Stream: TStream; data: Pointer;
  660. ElementSize: Integer; Count: Integer = 1);
  661. function WriteMotorolaNumber(Stream: TStream; data: Pointer;
  662. ElementSize: Integer; Count: Integer = 1): Integer;
  663. function ReadS0(Stream: TStream; out Str: string): Integer;
  664. procedure WriteS0(Stream: TStream; data: string);
  665. procedure WriteU4AsVX(Stream: TStream; data: Pointer; Count: Integer);
  666. function ReadVXAsU4(Stream: TStream; data: Pointer; Count: Integer = 1)
  667. : Integer;
  668. procedure ReverseByteOrder(ValueIn: Pointer; size: Integer; Count: Integer = 1);
  669. function ToDosPath(const Path: string): string;
  670. function ToUnixPath(const Path: string): string;
  671. function ID4ToInt(const id: TID4): Integer;
  672. // ChunkFind procedures
  673. procedure FindChunkById(AChunk: TLWChunk; data: Pointer; var Found: boolean);
  674. procedure FindSurfaceByName(AChunk: TLWChunk; AName: Pointer;
  675. var Found: boolean);
  676. procedure FindSurfaceByTag(AChunk: TLWChunk; ATag: Pointer; var Found: boolean);
  677. procedure FindVMapByName(AChunk: TLWChunk; AName: Pointer; var Found: boolean);
  678. procedure FindClipByClipIndex(AChunk: TLWChunk; AIndex: Pointer;
  679. var Found: boolean);
  680. function GetContentDir: TLWContentDir;
  681. // --------------------------------------------------------------------
  682. implementation
  683. // --------------------------------------------------------------------
  684. type
  685. PWord = ^Word;
  686. PLongWord = ^LongWord;
  687. var
  688. ChunkClasses: TList;
  689. ContentDir: TLWContentDir;
  690. function ToDosPath(const Path: string): string;
  691. var
  692. i: Integer;
  693. begin
  694. result := Path;
  695. for i := 1 to Length(result) do
  696. if result[i] = '/' then
  697. result[i] := '\';
  698. end;
  699. function ToUnixPath(const Path: string): string;
  700. var
  701. i: Integer;
  702. begin
  703. result := Path;
  704. for i := 1 to Length(result) do
  705. if result[i] = '\' then
  706. result[i] := '/';
  707. end;
  708. function GetContentDir: TLWContentDir;
  709. begin
  710. if ContentDir = nil then
  711. ContentDir := TLWContentDir.Create;
  712. result := ContentDir;
  713. end;
  714. procedure FindChunkById(AChunk: TLWChunk; data: Pointer; var Found: boolean);
  715. begin
  716. if AChunk.FID = PID4(data)^ then
  717. Found := true
  718. else
  719. Found := false;
  720. end;
  721. procedure FindClipByClipIndex(AChunk: TLWChunk; AIndex: Pointer;
  722. var Found: boolean);
  723. begin
  724. if (AChunk is TLWClip) and (TLWClip(AChunk).ClipIndex = PU2(AIndex)^) then
  725. Found := true;
  726. end;
  727. procedure FindSurfaceByName(AChunk: TLWChunk; AName: Pointer;
  728. var Found: boolean);
  729. begin
  730. if (AChunk is TLWSurf) and (TLWSurf(AChunk).Name = PString(AName)^) then
  731. Found := true;
  732. end;
  733. procedure FindSurfaceByTag(AChunk: TLWChunk; ATag: Pointer; var Found: boolean);
  734. begin
  735. if (AChunk is TLWSurf) and (TLWSurf(AChunk).surfid = PU2(ATag)^) then
  736. Found := true;
  737. end;
  738. procedure FindVMapByName(AChunk: TLWChunk; AName: Pointer; var Found: boolean);
  739. begin
  740. if (AChunk is TLWVMap) and (TLWVMap(AChunk).Name = PString(AName)^) then
  741. Found := true;
  742. end;
  743. function VecAdd(v1, v2: TVec12): TVec12;
  744. begin
  745. result[0] := v1[0] + v2[0];
  746. result[1] := v1[1] + v2[1];
  747. result[2] := v1[2] + v2[2];
  748. end;
  749. function VecSub(v1, v2: TVec12): TVec12;
  750. begin
  751. result[0] := v1[0] - v2[0];
  752. result[1] := v1[1] - v2[1];
  753. result[2] := v1[2] - v2[2];
  754. end;
  755. function VecCross(v1, v2: TVec12): TVec12;
  756. begin
  757. result[0] := v1[1] * v2[2] - v1[2] * v2[1];
  758. result[1] := v1[2] * v2[0] - v1[0] * v2[2];
  759. result[2] := v1[0] * v2[1] - v1[1] * v2[0];
  760. end;
  761. function VecDot(v1, v2: TVec12): TF4;
  762. begin
  763. result := v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
  764. end;
  765. function VecNorm(v: TVec12): TVec12;
  766. var
  767. mag: TF4;
  768. begin
  769. mag := Sqrt(VecDot(v, v));
  770. if mag > 0 then
  771. mag := 1 / mag;
  772. result[0] := v[0] * mag;
  773. result[1] := v[1] * mag;
  774. result[2] := v[2] * mag;
  775. end;
  776. function CalcPlaneNormal(v1, v2, v3: TVec12): TVec12;
  777. var
  778. e1, e2: TVec12;
  779. begin
  780. e1 := VecSub(v2, v1);
  781. e2 := VecSub(v3, v1);
  782. result := VecCross(e1, e2);
  783. result := VecNorm(result);
  784. end;
  785. procedure FindSurfByName(Chunk: TLWChunk; var Found: boolean);
  786. begin
  787. end;
  788. (*-----------------------------------------------------------------------------
  789. Procedure: GetChunkClasses
  790. Date: 08-Aug-2002
  791. Arguments: None
  792. Result: TClassList
  793. Singleton access for the chunk class list.
  794. -----------------------------------------------------------------------------*)
  795. function GetChunkClasses: TList;
  796. begin
  797. if ChunkClasses = nil then
  798. ChunkClasses := TList.Create;
  799. result := ChunkClasses;
  800. end;
  801. procedure UnRegisterChunkClasses;
  802. var
  803. i: Integer;
  804. begin
  805. with GetChunkClasses do
  806. for i := 0 to Count - 1 do
  807. UnregisterClass(TPersistentClass(Items[i]));
  808. end;
  809. (*-----------------------------------------------------------------------------
  810. Procedure: RegisterChunkClass
  811. Date: 08-Aug-2002
  812. Arguments: ChunkClass: TLWChunkClass
  813. Result: None
  814. Adds a user defined chunk class to the chunk class list.
  815. -----------------------------------------------------------------------------*)
  816. procedure RegisterChunkClass(ChunkClass: TLWChunkClass);
  817. begin
  818. GetChunkClasses.Add(ChunkClass);
  819. // if FindClass(ChunkClass.ClassName) <> nil then
  820. // UnRegisterClass(ChunkClass);
  821. // RegisterClass(ChunkClass);
  822. end;
  823. (*-----------------------------------------------------------------------------
  824. Procedure: GetChunkClass
  825. Date: 08-Aug-2002
  826. Arguments: ChunkID: TID4
  827. Result: TLWChunkClass
  828. Returns the chunk class associated with ChunkID.
  829. -----------------------------------------------------------------------------*)
  830. function GetChunkClass(ChunkID: TID4; ADefault: TLWChunkClass): TLWChunkClass;
  831. var
  832. i: Integer;
  833. begin
  834. if ADefault = nil then
  835. result := TLWChunk
  836. else
  837. result := ADefault;
  838. for i := 0 to ChunkClasses.Count - 1 do
  839. begin
  840. if TLWChunkClass(ChunkClasses.Items[i]).GetID = ChunkID then
  841. begin
  842. result := TLWChunkClass(ChunkClasses.Items[i]);
  843. Exit;
  844. end;
  845. end;
  846. end;
  847. (*-----------------------------------------------------------------------------
  848. Procedure: Tokenize
  849. Date: 08-Aug-2002
  850. Arguments: const Src: string; Delimiter: Char; Dst: TStrings
  851. Result: None
  852. Breaks up a string into TStrings items when the Delimiter character is
  853. encountered.
  854. -----------------------------------------------------------------------------*)
  855. procedure Tokenize(const Src: string; Delimiter: Char; Dst: TStrings);
  856. var
  857. i, L, SL: Integer;
  858. SubStr: string;
  859. begin
  860. if Dst = nil then
  861. Exit;
  862. L := Length(Src);
  863. if (L = 0) or (Dst = nil) then
  864. Exit;
  865. SubStr := '';
  866. for i := 1 to L do
  867. begin
  868. if (Src[i] <> Delimiter) then
  869. SubStr := SubStr + Src[i]
  870. else
  871. begin
  872. SL := Length(SubStr);
  873. if SL > 0 then
  874. begin
  875. Dst.Add(SubStr);
  876. SubStr := '';
  877. end;
  878. end;
  879. end;
  880. if Length(SubStr) > 0 then
  881. Dst.Add(SubStr);
  882. end;
  883. (*-----------------------------------------------------------------------------
  884. Procedure: LoadLW0FromStream
  885. Date: 08-Aug-2002
  886. Arguments: Stream: TStream; ReadCallback: TLWOReadCallback; UserData: Pointer
  887. Result: LongWord
  888. -----------------------------------------------------------------------------*)
  889. function LoadLW0FromStream(Stream: TStream; ReadCallback: TLWOReadCallback;
  890. UserData: Pointer): LongWord;
  891. var
  892. Chunk: TLWChunkRec;
  893. CurId: TID4;
  894. StartPos, CurSize: TU4;
  895. begin
  896. try
  897. Stream.Read(CurId, 4);
  898. ReadMotorolaNumber(Stream, @CurSize, 4);
  899. if UpperCase(string(CurId)) = 'FORM' then
  900. begin
  901. Stream.Read(CurId, 4);
  902. end
  903. else
  904. raise Exception.Create
  905. ('Invalid magic number. Not a valid Lightwave Object');
  906. with Stream do
  907. while Position < size do
  908. begin
  909. Read(Chunk, 8);
  910. ReverseByteOrder(@Chunk.size, 4);
  911. StartPos := Position;
  912. GetMem(Chunk.data, Chunk.size);
  913. Stream.Read(Chunk.data^, Chunk.size);
  914. if Assigned(ReadCallback) then
  915. ReadCallback(Chunk, UserData);
  916. FreeMem(Chunk.data, Chunk.size);
  917. Position := StartPos + Chunk.size + (StartPos + Chunk.size) mod 2;
  918. end;
  919. Stream.Free;
  920. result := High(LongWord);
  921. except
  922. On E: Exception do
  923. begin
  924. Stream.Free;
  925. result := 0;
  926. end;
  927. end;
  928. end;
  929. function LoadLWOFromFile(const AFilename: String;
  930. ReadCallback: TLWOReadCallback; UserData: Pointer): LongWord;
  931. var
  932. Stream: TStream;
  933. begin
  934. Stream := TFileStream.Create(AFilename, fmOpenRead);
  935. try
  936. result := LoadLW0FromStream(Stream, ReadCallback, UserData);
  937. finally
  938. Stream.Free;
  939. end;
  940. end;
  941. procedure ReverseByteOrder(ValueIn: Pointer; size: Integer; Count: Integer = 1);
  942. var
  943. W: Word;
  944. pB: PByte;
  945. Blo, Bhi: Byte;
  946. L: LongWord;
  947. i: Integer;
  948. begin
  949. i := 0;
  950. case size of
  951. 2:
  952. begin
  953. while i < Count do
  954. begin
  955. W := PU2Array(ValueIn)^[i];
  956. pB := @W;
  957. Blo := pB^;
  958. Inc(pB);
  959. Bhi := pB^;
  960. pB^ := Blo;
  961. Dec(pB);
  962. pB^ := Bhi;
  963. PU2Array(ValueIn)^[i] := W;
  964. Inc(i);
  965. end;
  966. end;
  967. 4:
  968. begin
  969. while i < Count do
  970. begin
  971. L := PU4Array(ValueIn)^[i];
  972. pB := @W;
  973. Blo := pB^;
  974. Inc(pB);
  975. Bhi := pB^;
  976. pB^ := Blo;
  977. Dec(pB);
  978. pB^ := Bhi;
  979. PU4Array(ValueIn)^[i] := L;
  980. Inc(i);
  981. end;
  982. end;
  983. else
  984. raise Exception.Create('Lightwave.ReverseByteOrder: Invalid Size = ' +
  985. IntToStr(size));
  986. end;
  987. end;
  988. procedure ReadMotorolaNumber(Stream: TStream; data: Pointer;
  989. ElementSize: Integer; Count: Integer = 1);
  990. begin
  991. Stream.Read(data^, Count * ElementSize);
  992. if (ElementSize = 2) or (ElementSize = 4) then
  993. ReverseByteOrder(data, ElementSize, Count);
  994. end;
  995. function WriteMotorolaNumber(Stream: TStream; data: Pointer;
  996. ElementSize: Integer; Count: Integer = 1): Integer;
  997. var
  998. TempData: Pointer;
  999. begin
  1000. result := 0;
  1001. if data <> nil then
  1002. begin
  1003. TempData := AllocMem(ElementSize * Count);
  1004. try
  1005. if (ElementSize = 2) or (ElementSize = 4) then
  1006. ReverseByteOrder(TempData, ElementSize, Count);
  1007. result := Stream.Write(data, Count * ElementSize);
  1008. except
  1009. on E: Exception do
  1010. begin
  1011. FreeMem(TempData, Count * ElementSize);
  1012. raise;
  1013. end;
  1014. end;
  1015. end;
  1016. end;
  1017. function ReadS0(Stream: TStream; out Str: string): Integer;
  1018. var
  1019. Buf: array [0 .. 1] of AnsiChar;
  1020. StrBuf: string;
  1021. begin
  1022. Stream.Read(Buf, 2);
  1023. StrBuf := '';
  1024. while Buf[1] <> #0 do
  1025. begin
  1026. StrBuf := StrBuf + string(Buf);
  1027. Stream.Read(Buf, 2);
  1028. end;
  1029. if Buf[0] <> #0 then
  1030. StrBuf := StrBuf + Char(Buf[0]);
  1031. Str := Copy(StrBuf, 1, Length(StrBuf));
  1032. result := Length(Str) + 1;
  1033. result := result + (result mod 2);
  1034. end;
  1035. function ValueOfVX(VX: Pointer): TU4;
  1036. var
  1037. TmpU2: TU2;
  1038. TmpU4: TU4;
  1039. begin
  1040. if PU1(VX)^ = $FF then
  1041. begin
  1042. TmpU4 := TU4(PU1(VX)^) and $FFFFFFF0;
  1043. ReverseByteOrder(@TmpU4, 4);
  1044. end
  1045. else
  1046. begin
  1047. TmpU2 := TU2(PU2(VX)^);
  1048. ReverseByteOrder(@TmpU2, 2);
  1049. TmpU4 := TmpU2;
  1050. end;
  1051. result := TmpU4;
  1052. end;
  1053. function ReadVXAsU4(Stream: TStream; data: Pointer; Count: Integer = 1)
  1054. : Integer;
  1055. var
  1056. i, ReadCount: Integer;
  1057. BufByte: Byte;
  1058. TempU2: TU2;
  1059. begin
  1060. ReadCount := 0;
  1061. for i := 0 to Count - 1 do
  1062. begin
  1063. Stream.Read(BufByte, 1);
  1064. Stream.Position := Stream.Position - 1;
  1065. if BufByte = 255 then
  1066. begin
  1067. Stream.Read(data^, SizeOf(TU4));
  1068. PU4Array(data)^[i] := PU4Array(data)^[i] and $FFFFFFF0;
  1069. ReverseByteOrder(data, SizeOf(TU4));
  1070. Inc(ReadCount, 4);
  1071. end
  1072. else
  1073. begin
  1074. Stream.Read(TempU2, SizeOf(TU2));
  1075. ReverseByteOrder(@TempU2, SizeOf(TU2));
  1076. PU4Array(data)^[i] := TempU2;
  1077. Inc(ReadCount, 2);
  1078. end;
  1079. end;
  1080. result := ReadCount;
  1081. end;
  1082. function ReadVXAsU2(Stream: TStream; data: Pointer; Count: Integer = 1)
  1083. : Integer;
  1084. var
  1085. i, ReadCount: Integer;
  1086. BufByte: Byte;
  1087. TempU2: TU2;
  1088. begin
  1089. ReadCount := 0;
  1090. for i := 0 to Count - 1 do
  1091. begin
  1092. Stream.Read(BufByte, 1);
  1093. Stream.Position := Stream.Position - 1;
  1094. if BufByte = 255 then
  1095. begin
  1096. Stream.Position := Stream.Position + 4;
  1097. PU2Array(data)^[i] := 0;
  1098. Inc(ReadCount, 4);
  1099. end
  1100. else
  1101. begin
  1102. Stream.Read(TempU2, SizeOf(TU2));
  1103. ReverseByteOrder(@TempU2, SizeOf(TU2));
  1104. PU2Array(data)^[i] := TempU2;
  1105. Inc(ReadCount, 2);
  1106. end;
  1107. end;
  1108. result := ReadCount;
  1109. end;
  1110. procedure WriteS0(Stream: TStream; data: string);
  1111. begin
  1112. // ToDo: WriteS0
  1113. end;
  1114. procedure WriteU4AsVX(Stream: TStream; data: Pointer; Count: Integer);
  1115. var
  1116. i: Integer;
  1117. TempU2: TU2;
  1118. begin
  1119. for i := 0 to Count - 1 do
  1120. begin
  1121. if PU4Array(data)^[i] < 65280 then
  1122. begin
  1123. TempU2 := PU4Array(data)^[i];
  1124. WriteMotorolaNumber(Stream, @TempU2, SizeOf(TU2));
  1125. end
  1126. else
  1127. WriteMotorolaNumber(Stream, data, SizeOf(TU4));
  1128. end;
  1129. end;
  1130. type
  1131. PInteger = ^Integer;
  1132. function ID4ToInt(const id: TID4): Integer;
  1133. var
  1134. TmpId: AnsiString;
  1135. begin
  1136. TmpId := id;
  1137. TmpId := AnsiString(UpperCase(string(id)));
  1138. result := PInteger(@TmpId)^;
  1139. end;
  1140. (*********************************** TLWChunk ********************************)
  1141. destructor TLWChunk.Destroy;
  1142. begin
  1143. Clear;
  1144. inherited;
  1145. end;
  1146. procedure TLWChunk.Clear;
  1147. begin
  1148. FreeMem(FData, FSize);
  1149. FSize := 0;
  1150. FData := nil;
  1151. end;
  1152. class function TLWChunk.GetID: TID4;
  1153. begin
  1154. result := #0#0#0#0;
  1155. end;
  1156. procedure TLWChunk.LoadData(AStream: TStream; DataStart, DataSize: LongWord);
  1157. begin
  1158. GetMem(FData, DataSize);
  1159. AStream.Read(PByteArray(FData)^[0], DataSize);
  1160. end;
  1161. procedure TLWChunk.LoadFromStream(AStream: TStream);
  1162. var
  1163. DataStart: Integer;
  1164. DataSize: TU4;
  1165. begin
  1166. with AStream do
  1167. begin
  1168. ReadMotorolaNumber(AStream, @DataSize, 4);
  1169. DataStart := Position;
  1170. FSize := DataSize;
  1171. LoadData(AStream, DataStart, DataSize);
  1172. Position := Cardinal(DataStart) + DataSize +
  1173. (Cardinal(DataStart) + DataSize) mod 2;
  1174. end;
  1175. end;
  1176. (********************************* TLWChunkList *******************************)
  1177. constructor TLWChunkList.Create(AOwnsItems: boolean; AOwner: TObject);
  1178. begin
  1179. inherited Create;
  1180. FOwnsItems := AOwnsItems;
  1181. FOwner := AOwner;
  1182. end;
  1183. destructor TLWChunkList.Destroy;
  1184. begin
  1185. Clear;
  1186. inherited;
  1187. end;
  1188. procedure TLWChunkList.Clear;
  1189. begin
  1190. while Count > 0 do
  1191. Delete(Count - 1);
  1192. inherited;
  1193. end;
  1194. procedure TLWChunkList.Delete(Index: Integer);
  1195. begin
  1196. if FOwnsItems then
  1197. Items[Index].Free;
  1198. inherited Delete(Index);
  1199. end;
  1200. function TLWChunkList.GetItem(Index: Integer): TLWChunk;
  1201. begin
  1202. result := TLWChunk(inherited Items[Index]);
  1203. end;
  1204. (******************************** TLWObjectFile *******************************)
  1205. constructor TLWObjectFile.Create;
  1206. begin
  1207. inherited;
  1208. end;
  1209. destructor TLWObjectFile.Destroy;
  1210. begin
  1211. FreeAndNil(FChunks);
  1212. inherited;
  1213. end;
  1214. function TLWObjectFile.GetChunks: TLWChunkList;
  1215. begin
  1216. if FChunks = nil then
  1217. FChunks := TLWChunkList.Create(true, Self);
  1218. result := FChunks;
  1219. end;
  1220. function TLWObjectFile.GetCount: Integer;
  1221. begin
  1222. result := Chunks.Count;
  1223. end;
  1224. function TLWObjectFile.GetSurfaceByName(Index: string): TLWSurf;
  1225. var
  1226. SurfIdx: Integer;
  1227. begin
  1228. SurfIdx := Chunks.FindChunk(@FindSurfaceByName, @Index, 0);
  1229. if SurfIdx <> -1 then
  1230. result := TLWSurf(Chunks[SurfIdx])
  1231. else
  1232. result := nil;
  1233. end;
  1234. function TLWObjectFile.GetSurfaceByTag(Index: TU2): TLWSurf;
  1235. var
  1236. TagName: string;
  1237. begin
  1238. TagName := TagToName(Index);
  1239. result := SurfaceByName[TagName];
  1240. end;
  1241. procedure TLWObjectFile.LoadFromFile(const AFilename: string);
  1242. var
  1243. Stream: TMemoryStream;
  1244. begin
  1245. Stream := TMemoryStream.Create;
  1246. try
  1247. Stream.LoadFromFile(AFilename);
  1248. LoadFromStream(Stream);
  1249. Stream.Free;
  1250. FFileName := AFilename;
  1251. except
  1252. on E: Exception do
  1253. begin
  1254. Stream.Free;
  1255. raise;
  1256. end;
  1257. end;
  1258. end;
  1259. procedure TLWObjectFile.LoadFromStream(AStream: TStream);
  1260. var
  1261. CurId: TID4;
  1262. CurSize: LongWord;
  1263. CurPnts, CurPols, CurItems: TLWChunkList;
  1264. begin
  1265. CurPols := nil;
  1266. CurPnts := nil;
  1267. AStream.Read(CurId, 4);
  1268. ReadMotorolaNumber(AStream, @CurSize, 4);
  1269. if UpperCase(string(CurId)) = 'FORM' then
  1270. begin
  1271. AStream.Read(CurId, 4);
  1272. if CurId <> 'LWO2' then
  1273. raise Exception.Create
  1274. ('Only Version 6.0+ version objects are supported.');
  1275. end
  1276. else
  1277. raise Exception.Create
  1278. ('Invalid magic number. Not a valid Lightwave Object');
  1279. CurItems := Chunks;
  1280. while AStream.Position < AStream.size do
  1281. begin
  1282. AStream.Read(CurId, 4);
  1283. if (CurId = ID_PTAG) then
  1284. begin
  1285. CurPols.Add(GetChunkClass(CurId, TLWChunk).Create);
  1286. with CurPols[CurPols.Count - 1] do
  1287. begin
  1288. FID := CurId;
  1289. LoadFromStream(AStream);
  1290. end;
  1291. end
  1292. else if (CurId = ID_VMAP) or (CurId = ID_VMAD) then
  1293. begin
  1294. CurPnts.Add(GetChunkClass(CurId, TLWChunk).Create);
  1295. with CurPnts[CurPnts.Count - 1] do
  1296. begin
  1297. FID := CurId;
  1298. LoadFromStream(AStream);
  1299. end;
  1300. end
  1301. else
  1302. begin
  1303. if (CurId = ID_LAYR) or (CurId = ID_SURF) or (CurId = ID_TAGS) or
  1304. (CurId = ID_CLIP) then
  1305. CurItems := Chunks;
  1306. CurItems.Add(GetChunkClass(CurId, TLWChunk).Create);
  1307. with CurItems[CurItems.Count - 1] do
  1308. begin
  1309. FID := CurId;
  1310. LoadFromStream(AStream);
  1311. end;
  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. with Items[Items.Count - 1] do
  1642. begin
  1643. FID := CurId;
  1644. LoadFromStream(AStream);
  1645. end;
  1646. end;
  1647. end;
  1648. (*********************************** TLWPTag **********************************)
  1649. constructor TLWPTag.Create;
  1650. begin
  1651. inherited;
  1652. end;
  1653. function TLWPTag.AddTag(Value: TU2): Integer;
  1654. var
  1655. i, L: Integer;
  1656. begin
  1657. result := -1;
  1658. L := Length(FTags);
  1659. for i := 0 to L - 1 do
  1660. if Value = FTags[i] then
  1661. begin
  1662. result := i;
  1663. Exit;
  1664. end;
  1665. if result = -1 then
  1666. begin
  1667. SetLength(FTags, L + 1);
  1668. FTags[L] := Value;
  1669. result := L;
  1670. end;
  1671. end;
  1672. procedure TLWPTag.Clear;
  1673. begin
  1674. SetLength(FTagMaps, 0);
  1675. SetLength(FTags, 0);
  1676. end;
  1677. function TLWPTag.GetPolsByTag(tag: TU2; var PolyIndices: TU2DynArray): Integer;
  1678. var
  1679. i: Integer;
  1680. procedure AddPoly(Value: TU2);
  1681. var
  1682. L: Integer;
  1683. begin
  1684. L := Length(PolyIndices);
  1685. SetLength(PolyIndices, L + 1);
  1686. PolyIndices[L] := Value;
  1687. end;
  1688. begin
  1689. for i := 0 to TagMapCount - 1 do
  1690. if TagMaps[i].tag = tag then
  1691. AddPoly(TagMaps[i].poly);
  1692. result := Length(PolyIndices);
  1693. end;
  1694. class function TLWPTag.GetID: TID4;
  1695. begin
  1696. result := ID_PTAG;
  1697. end;
  1698. function TLWPTag.GetTag(AIndex: Integer): TU2;
  1699. begin
  1700. ValidateTagInfo;
  1701. result := FTags[AIndex];
  1702. end;
  1703. function TLWPTag.GetTagCount: Integer;
  1704. begin
  1705. ValidateTagInfo;
  1706. result := Length(FTags);
  1707. end;
  1708. function TLWPTag.GetTagMapCount: Integer;
  1709. begin
  1710. result := Length(FTagMaps) div 2;
  1711. end;
  1712. function TLWPTag.GetTagMaps(AIndex: Integer): TLWPolyTagMap;
  1713. begin
  1714. result := PLWPolyTagMap(@FTagMaps[AIndex * 2])^;
  1715. end;
  1716. procedure TLWPTag.LoadData(AStream: TStream; DataStart, DataSize: LongWord);
  1717. var
  1718. Idx: Integer;
  1719. begin
  1720. Idx := 0;
  1721. with AStream do
  1722. begin
  1723. Read(FMapType, 4);
  1724. SetLength(FTagMaps, (DataSize - 4) div 2);
  1725. while Cardinal(Position) < (DataStart + DataSize) do
  1726. begin
  1727. ReadVXAsU2(AStream, @FTagMaps[Idx]);
  1728. ReadMotorolaNumber(AStream, @FTagMaps[Idx + 1], 2);
  1729. Inc(Idx, 2);
  1730. end;
  1731. // correct length guestimate errors if any
  1732. if (Idx + 1) < Length(FTagMaps) then
  1733. SetLength(FTagMaps, Idx + 1);
  1734. end;
  1735. end;
  1736. procedure TLWPTag.ValidateTagInfo;
  1737. var
  1738. i: Integer;
  1739. begin
  1740. if Length(FTags) > 0 then
  1741. Exit;
  1742. for i := 0 to TagMapCount - 1 do
  1743. AddTag(TagMaps[i].tag);
  1744. end;
  1745. (******************************** TLWParentChunk ******************************)
  1746. procedure TLWParentChunk.Clear;
  1747. begin
  1748. FreeAndNil(FItems);
  1749. inherited;
  1750. end;
  1751. function TLWParentChunk.GetFloatParam(Param: TID4): Single;
  1752. var
  1753. pdata: Pointer;
  1754. begin
  1755. pdata := ParamAddr[Param];
  1756. if pdata <> nil then
  1757. begin
  1758. result := PF4(pdata)^;
  1759. ReverseByteOrder(@result, 4);
  1760. end
  1761. else
  1762. result := 0.0;
  1763. end;
  1764. function TLWParentChunk.GetItems: TLWChunkList;
  1765. begin
  1766. if FItems = nil then
  1767. FItems := TLWChunkList.Create(true, Self);
  1768. result := FItems;
  1769. end;
  1770. function TLWParentChunk.GetLongParam(Param: TID4): LongWord;
  1771. var
  1772. pdata: Pointer;
  1773. begin
  1774. pdata := ParamAddr[Param];
  1775. if pdata <> nil then
  1776. begin
  1777. result := PU4(pdata)^;
  1778. ReverseByteOrder(@result, 4);
  1779. end
  1780. else
  1781. result := 0;
  1782. end;
  1783. function TLWParentChunk.GetParamAddr(Param: TID4): Pointer;
  1784. var
  1785. Idx: Integer;
  1786. begin
  1787. result := nil;
  1788. Idx := Items.FindChunk(@FindChunkById, @Param, 0);
  1789. if Idx <> -1 then
  1790. result := Items[Idx].data;
  1791. end;
  1792. function TLWPols.GetPolsByPntIdx(VertIdx: TU2;
  1793. var VertPolys: TU2DynArray): Integer;
  1794. var
  1795. i, j, L: Integer;
  1796. begin
  1797. L := 0;
  1798. if Length(VertPolys) > 0 then
  1799. SetLength(VertPolys, 0);
  1800. for i := 0 to PolsCount - 1 do
  1801. begin
  1802. for j := 1 to Indices[PolsByIndex[i]] do
  1803. begin
  1804. if Indices[PolsByIndex[i] + j] = VertIdx then
  1805. begin
  1806. L := Length(VertPolys);
  1807. SetLength(VertPolys, L + 1);
  1808. VertPolys[L] := i;
  1809. end;
  1810. end;
  1811. end;
  1812. result := L;
  1813. end;
  1814. function TLWChunkList.Add(AChunk: TLWChunk): Integer;
  1815. begin
  1816. if (FOwner <> nil) and (FOwner is TLWParentChunk) then
  1817. AChunk.FParentChunk := TLWParentChunk(FOwner);
  1818. AChunk.FOwner := Self;
  1819. result := inherited Add(AChunk);
  1820. end;
  1821. procedure TLWPols.CalcPntsNormals;
  1822. var
  1823. i, j, k, PntIdx, PolyIdx, SurfIdx: Integer;
  1824. Pnts: TLWPnts;
  1825. // PTags: TLWPTag;
  1826. TmpAddr: Pointer;
  1827. sman: TF4;
  1828. begin
  1829. // Todo: CalcPntsNormals
  1830. if IndiceCount = 0 then
  1831. Exit;
  1832. with ParentChunk as TLWLayr do
  1833. Pnts := TLWPnts(Items[Items.FindChunk(@FindChunkById, @ID_PNTS, 0)]);
  1834. for PolyIdx := 0 to PolsCount - 1 do
  1835. begin
  1836. i := PolsByIndex[PolyIdx];
  1837. SurfIdx := RootChunks.FindChunk(@FindSurfaceByTag,
  1838. @FPolsInfo[PolyIdx].surfid);
  1839. TmpAddr := TLWSurf(RootChunks[SurfIdx]).ParamAddr[ID_SMAN];
  1840. if TmpAddr <> nil then
  1841. begin
  1842. sman := PF4(TmpAddr)^;
  1843. ReverseByteOrder(@sman, 4);
  1844. end
  1845. else
  1846. sman := 0;
  1847. for j := 1 to Indices[i] do
  1848. begin
  1849. FPolsInfo[PolyIdx].vnorms[j - 1] := FPolsInfo[PolyIdx].norm;
  1850. if sman <= 0 then
  1851. continue;
  1852. PntIdx := Indices[i + j];
  1853. for k := 0 to Pnts.PntsInfo[PntIdx].npols - 1 do
  1854. begin
  1855. if Pnts.PntsInfo[PntIdx].pols[k] = PolyIdx then
  1856. continue;
  1857. if ArcCos(VecDot(FPolsInfo[PolyIdx].norm,
  1858. FPolsInfo[Pnts.PntsInfo[PntIdx].pols[k]].norm)) > sman then
  1859. continue;
  1860. FPolsInfo[PolyIdx].vnorms[j - 1] :=
  1861. VecAdd(FPolsInfo[PolyIdx].vnorms[j - 1],
  1862. FPolsInfo[Pnts.PntsInfo[PntIdx].pols[k]].norm);
  1863. end;
  1864. FPolsInfo[PolyIdx].vnorms[j - 1] :=
  1865. VecNorm(FPolsInfo[PolyIdx].vnorms[j - 1]);
  1866. end;
  1867. end;
  1868. end;
  1869. function TLWChunk.GetRootChunks: TLWChunkList;
  1870. var
  1871. Parent: TLWParentChunk;
  1872. begin
  1873. result := nil;
  1874. if (FParentChunk = nil) then
  1875. begin
  1876. if (FOwner is TLWChunkList) then
  1877. begin
  1878. result := FOwner;
  1879. Exit;
  1880. end;
  1881. end
  1882. else
  1883. begin
  1884. Parent := FParentChunk;
  1885. while not(Parent.ParentChunk = nil) do
  1886. Parent := Parent.ParentChunk;
  1887. result := Parent.Owner;
  1888. end;
  1889. end;
  1890. function TLWChunkList.FindChunk(ChunkFind: TLWChunkFind; Criteria: Pointer;
  1891. StartIndex: Integer): Integer;
  1892. var
  1893. Found: boolean;
  1894. begin
  1895. Found := false;
  1896. result := -1;
  1897. while (StartIndex < Count) and (not Found) do
  1898. begin
  1899. ChunkFind(Items[StartIndex], Criteria, Found);
  1900. if Found then
  1901. begin
  1902. result := StartIndex;
  1903. Exit;
  1904. end;
  1905. Inc(StartIndex);
  1906. end;
  1907. end;
  1908. function TLWChunk.GetIndex: Integer;
  1909. begin
  1910. result := Owner.IndexOf(Self);
  1911. end;
  1912. procedure TLWChunk.Loaded;
  1913. begin
  1914. // do nothing
  1915. end;
  1916. procedure TLWChunkList.Loaded;
  1917. var
  1918. i: Integer;
  1919. begin
  1920. for i := 0 to Count - 1 do
  1921. begin
  1922. Items[i].Loaded;
  1923. end;
  1924. end;
  1925. function TLWParentChunk.GetVec3Param(Param: TID4): TVec12;
  1926. var
  1927. pdata: Pointer;
  1928. begin
  1929. pdata := ParamAddr[Param];
  1930. if pdata <> nil then
  1931. begin
  1932. result := PVec12(pdata)^;
  1933. ReverseByteOrder(@result, 4, 3);
  1934. end
  1935. else
  1936. begin
  1937. result[0] := 0;
  1938. result[1] := 1;
  1939. result[2] := 2;
  1940. end;
  1941. end;
  1942. function TLWParentChunk.GetVXParam(Param: TID4): Word;
  1943. var
  1944. pdata: Pointer;
  1945. begin
  1946. pdata := ParamAddr[Param];
  1947. if pdata <> nil then
  1948. result := ValueOfVX(pdata)
  1949. else
  1950. result := 0;
  1951. end;
  1952. function TLWParentChunk.GetWordParam(Param: TID4): Word;
  1953. var
  1954. pdata: Pointer;
  1955. begin
  1956. pdata := ParamAddr[Param];
  1957. if pdata <> nil then
  1958. begin
  1959. result := PU4(pdata)^;
  1960. ReverseByteOrder(@result, 2);
  1961. end
  1962. else
  1963. result := 0;
  1964. end;
  1965. procedure TLWParentChunk.Loaded;
  1966. begin
  1967. Items.Loaded;
  1968. end;
  1969. procedure TLWPols.Loaded;
  1970. begin
  1971. inherited;
  1972. CalcPntsNormals;
  1973. end;
  1974. function TLWObjectFile.TagToName(tag: TU2): string;
  1975. var
  1976. TagsIdx: Integer;
  1977. begin
  1978. TagsIdx := Chunks.FindChunk(@FindChunkById, @ID_TAGS);
  1979. if TagsIdx <> -1 then
  1980. result := TLWTags(Chunks[TagsIdx]).TagToName(tag);
  1981. end;
  1982. (******************** TLWClip ********************)
  1983. class function TLWClip.GetID: TID4;
  1984. begin
  1985. result := ID_CLIP;
  1986. end;
  1987. procedure TLWClip.LoadData(AStream: TStream; DataStart, DataSize: LongWord);
  1988. var
  1989. CurId: TID4;
  1990. begin
  1991. ReadMotorolaNumber(AStream, @FClipIndex, 4);
  1992. while Cardinal(AStream.Position) < (DataStart + DataSize) do
  1993. begin
  1994. AStream.Read(CurId, 4);
  1995. Items.Add(GetChunkClass(CurId, TLWSubChunk).Create);
  1996. with Items[Items.Count - 1] do
  1997. begin
  1998. FID := CurId;
  1999. LoadFromStream(AStream);
  2000. end;
  2001. end;
  2002. end;
  2003. // TLWContentDir
  2004. (* function TLWContentDir.ContentSearch(AFilename: string): string;
  2005. var
  2006. i: Integer;
  2007. begin
  2008. if not FileExists(AFilename) then
  2009. begin
  2010. result := ExtractFileName(AFilename);
  2011. if not FileExists(result) then
  2012. begin
  2013. for i := 0 to SubDirs.Count - 1 do
  2014. begin
  2015. if FileExists(Root+'\'+SubDirs[i]+'\'+result) then
  2016. begin
  2017. result:=Root+'\'+SubDirs[i]+'\'+result;
  2018. Exit;
  2019. end;
  2020. end;
  2021. result := '';
  2022. end;
  2023. end;
  2024. end;
  2025. *)
  2026. destructor TLWContentDir.Destroy;
  2027. begin
  2028. FreeAndNil(FSubDirs);
  2029. inherited;
  2030. end;
  2031. function TLWContentDir.FindContent(AFilename: string): string;
  2032. var
  2033. i: Integer;
  2034. begin
  2035. if not FileExists(AFilename) then
  2036. begin
  2037. result := ExtractFileName(AFilename);
  2038. if not FileExists(result) then
  2039. begin
  2040. for i := 0 to SubDirs.Count - 1 do
  2041. begin
  2042. if FileExists(Root + '\' + SubDirs[i] + '\' + result) then
  2043. begin
  2044. result := Root + '\' + SubDirs[i] + '\' + result;
  2045. Exit;
  2046. end;
  2047. end;
  2048. result := '';
  2049. end;
  2050. end;
  2051. end;
  2052. function TLWContentDir.GetSubDirs: TStrings;
  2053. begin
  2054. if FSubDirs = nil then
  2055. FSubDirs := TStringList.Create;
  2056. result := FSubDirs;
  2057. end;
  2058. procedure TLWContentDir.SetRoot(const Value: string);
  2059. begin
  2060. FRoot := Value;
  2061. end;
  2062. procedure TLWContentDir.SetSubDirs(const Value: TStrings);
  2063. begin
  2064. SubDirs.Assign(Value);
  2065. end;
  2066. initialization
  2067. // Pnts
  2068. RegisterChunkClass(TLWPnts);
  2069. // Pols
  2070. RegisterChunkClass(TLWPols);
  2071. // VMap
  2072. RegisterChunkClass(TLWVMap);
  2073. // Tags
  2074. RegisterChunkClass(TLWTags);
  2075. // PTAG
  2076. RegisterChunkClass(TLWPTag);
  2077. // SURF
  2078. RegisterChunkClass(TLWSurf);
  2079. // LAYR
  2080. RegisterChunkClass(TLWLayr);
  2081. // CLIP
  2082. RegisterChunkClass(TLWClip);
  2083. finalization
  2084. // UnRegisterChunkClasses;
  2085. FreeAndNil(ChunkClasses);
  2086. FreeAndNil(ContentDir);
  2087. end.