123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469 |
- //
- // The multimedia graphics platform GLScene https://github.com/glscene
- //
- unit Formats.LWO;
- (* =============================================================
- This unit provides functions, constants and now classes for use in
- working with Lightwave3D Object files.
- Chunk ID constants are defined for all of the Chunk IDs listed
- in the Lightwave 7.5 sdk.
- It is important to note that this is a constant work-in-progress
- and as such there are omissions and may be errors. Feedback and
- suggestions would be appreciated.
- There are two ways of using this unit. The first uses user-defines
- callbacks to handle parsing lwo chunk data. The second way uses
- object orientation.
- Loading LWO chunk data via callbacks
- A function is provided for loading a Lightwave object from a file.
- The Loader itself uses a callback mechanism for the loading of
- Lightwave object chunks. The callback is called for every chunk
- (with the exception of the FORM and LWOB or LWO2 chunks).
- The Chunk struct passed in the callback contains members for the
- chunk ID, chunk size and pointer to chunk data. This data is
- untouched internally so any parsing and numeric formatting
- is up to you. This provides maximum flexibility and allows you to
- handle the data that you need without loading the entire object
- into ram first.
- The chunk data memory is freed upon the return of the callback
- so do not keep a reference to the chunk data. Copy it to your own
- storage.
- function LoadLW0(const Filename: string; ReadProc: TLWOReadProc;
- UserData: Pointer): LongWord; cdecl;
- Filename: The fully qualified filename of the file to be
- loaded.
- ReadCallback: The address of a TLWOReadCallback procedure
- defined as:
- TLWOReadCallback = procedure(Chunk: TLWChunk;
- UserData: Pointer); cdecl;
- This procedure will be called for every chunk
- encountered in the Lightwave object file. The
- Chunk parameter is the chunk struct of the chunk
- being loaded. UserData is the pointer supplied
- in the original call to LoadLWO (see below).
- UserData: A pointer to user supplied data to be passed
- in the ReadCallback.
- A non-zero results indicates that the object file was parsed
- successfully.
- Loading LWO chunks via objects
- ============================
- To load data from a lightwave object file, create an instance of
- TLWObjectFile and call its LoadFromFile method.
- The data can then be accessed with the Chunks array property and
- iterated in combination with the ChunkCount property.
- Chunk data is parsed and interfaced by descendents of the TLWChunk
- class. I have made handlers for the following chunk types:
- TLWLayr Modeler Layer chunk
- TLWPnts Points chunk
- TLWPols Polygons chunk
- TLWPTag Polygon tag mapping
- TLWSurf Surface subchunk container
- TLWTags Tags (Name tag strings for named items)
- TLWVMap Vertex Mapping
- The data for chunks without handlers can be gotten at with the
- Data and Size properties of the TLWChunk. Data is a pointer to
- the start of the chunk data. This data is unparsed.
- Data is nil for descendents.
- This should provide enough to move geometry into your favourite
- delphi-based 3d engine.
- Making chunk handler objects
- ============================
- All chunk types are derived from TLWChunk in the following manner:
- TLWChunk
- ex:
- TLWPTag <- PTAG chunk type. polygon tag map.
- TLWParentChunk <- A base class for chunks that can contain other chunks.
- This is not necessarily how the data is stored in
- the file but more for ease of access to data.
- ex:
- TLWPnts <- PNTS chunk type (points)
- TLWLayr <- LAYR chunk type (modeler layer)
- TLWSurf <- SURF chunk type (constains surface attributes as sub chunks)
- TLWSubChunk <- A base class for chunks whose max data len is 65536 bytes.
- TLWDiff <- DIFF subchunk type (diffuse surface parameter)
- TLWSpec <- SPEC subchunk type (specularity surface parameter)...
- etc.
- Each descendent of TLWChunk or TLWSubChunk is required to override
- the GetID class function, the LoadData method and the Clear method
- to provide custom handling for chunktype data.
- ex:
- ...
- type
- TLWPnts = class (TLWParentChunk)
- private
- FPoints: TVEC12DynArray;
- function GetCount: LongWord;
- protected
- procedure Clear; override;
- procedure LoadData(AStream: TStream; DataStart, DataSize: LongWord); override;
- public
- class function GetID: TID4; override;
- function GetVMap(VMapID: TID4; out VMap: TLWVMap): boolean;
- property Count: LongWord read GetCount;
- property Points: TVEC12DynArray read FPoints;
- end;
- ...
- // Return the the chunk id that is the target of this handler
- class function TLWPnts.GetID: TID4;
- begin
- result := ID_PNTS;
- end;
- // Load the point data - the stream is already positioned at the start of the chunk data
- procedure TLWPnts.LoadData(AStream: TStream; DataStart, DataSize: LongWord);
- begin
- SetLength(FPoints,DataSize div 12); // allocate storage for DataSize div 12 points
- ReadMotorolaNumber(AStream,@FPoints[0],4,DataSize div 4); // read the point data
- end;
- // Cleanup - Free any memory that you've allocated
- procedure TLWPnts.Clear;
- begin
- SetLength(FPoints,0);
- end;
- Utility Functions
- =================
- A function is provided for converting an array of numbers between
- Motorola and Intel format (big endian <-> little endian). Converting
- only needs to be done for numeric types that are of 2 or 4 byte
- lengths.
- procedure ReverseByteOrder(ValueIn: Pointer; Size: integer; Count: integer = 1);
- ValueIn: The address of a number or array of numbers to have their
- bytes swapped.
- Size: The size in bytes of each numeric type.
- Count: The count of numbers in the numbers array. The default
- value is 1.
- Two routines are provided for reading and writing big endian
- (Motorola and misc other processor vendors ) numbers to and from a
- stream. These routines handle 2 and 4 byte numeric types and can
- also handle arrays.
- procedure ReadMotorolaNumber(Stream: TStream; Data: Pointer;
- ElementSize: integer; Count: integer = 1);
- function WriteMotorolaNumber(Stream: TStream; Data: Pointer;
- ElementSize: integer; Count: integer = 1): Integer;
- Each take a valid TStream descendent, a pointer to the numeric data,
- the element size of the data elements (either 2 or 4) and the array
- element count if sending an array. The default count is 1.
- Notes for improvement of this unit:
- - A version ID tag should be visible to all chunks in order to
- provide handling for Lightwave pre 6.0 object files.
- - Chunk type handlers should leave memory allocation to
- the base class (TLWChunk) and act more as an interface
- to the data pointed to by Data in TLWChunk. This would
- keep memory allocation very efficient and make implementing
- chunk handlers even easier.
- Author: Brian Johns [email protected]
- Purpose: Lightwave object support unit for Delphi.
- Notes: For the Lightwave Object File Format documentation please refer to
- http://www.lightwave3d.com/developer.
- Lightwave3D is a registered trademark of Newtek Incorporated.
- ===================================================================== *)
- interface
- {$I GLScene.inc}
- uses
- System.Classes,
- System.SysUtils,
- System.IOUtils,
- System.Math,
- GLS.VectorGeometry;
- type
- TID4 = array [0 .. 3] of AnsiChar;
- PID4 = ^TID4;
- TID4DynArray = array of TID4;
- const
- ID_NULL = '#0#0#0#0'; // NULL ID
- ID_LWSC: TID4 = 'LWSC'; // Lightwave scene file
- ID_FORM: TID4 = 'FORM'; // IFF Form
- ID_LWOB: TID4 = 'LWOB'; // Lightwave Object version 1.0 - 5.x
- ID_LWLO: TID4 = 'LWLO'; // Lightwave Layered Object
- ID_LAYR: TID4 = 'LAYR'; // LAYER
- ID_PNTS: TID4 = 'PNTS'; // Points chunk
- ID_SRFS: TID4 = 'SRFS'; // Surface Names chunk
- ID_POLS: TID4 = 'POLS'; // Polygons chunk
- ID_CRVS: TID4 = 'CRVS'; // Curves chunk
- ID_PCHS: TID4 = 'PCHS'; // Patches chunk
- ID_SURF: TID4 = 'SURF'; // Surfaces chunk
- ID_COLR: TID4 = 'COLR'; // Color chunk
- ID_FLAG: TID4 = 'FLAG'; // Surface Flags
- ID_LUMI: TID4 = 'LUMI'; // Luminosity
- ID_DIFF: TID4 = 'DIFF'; // Diffuse
- ID_SPEC: TID4 = 'SPEC'; // Specular
- ID_REFL: TID4 = 'REFL'; // Reflective
- ID_TRAN: TID4 = 'TRAN'; // Transparency
- ID_VLUM: TID4 = 'VLUM'; // Luminosity
- ID_VDIF: TID4 = 'VDIF'; // Diffuse
- ID_VSPC: TID4 = 'VSPC'; // Specularity
- ID_VRFL: TID4 = 'VRFL'; // Reflective
- ID_VTRN: TID4 = 'VTRN'; // Transparency
- ID_GLOS: TID4 = 'GLOS'; // Glossiness SmallInt
- ID_SIDE: TID4 = 'SIDE'; // Sidedness
- ID_RFLT: TID4 = 'RFLT'; // REFLECTION MODE (PRE 6.0)
- ID_RFOP: TID4 = 'RFOP'; // REFLECTION OPTIONS
- ID_RIMG: TID4 = 'RIMG'; // REFLECTION IMAGE
- ID_RSAN: TID4 = 'RSAN'; // REFLECTION MAP SEAM ANGLE
- ID_RIND: TID4 = 'RIND'; // REFRACTIVE INDEX
- ID_EDGE: TID4 = 'EDGE'; // EDGE TRANSPARENCY THRESHOLD
- ID_SMAN: TID4 = 'SMAN'; // SMOOTHING ANGLE RADIANS
- ID_ALPH: TID4 = 'ALPH'; // ALPHA MODE
- ID_CTEX: TID4 = 'CTEX'; // COLOR TEXTURE
- ID_DTEX: TID4 = 'DTEX'; // DIFFUSE TEXTURE
- ID_STEX: TID4 = 'STEX'; // SPECULAR TEXTURE
- ID_RTEX: TID4 = 'RTEX'; // REFLECTIION TEXTURE
- ID_TTEX: TID4 = 'TTEX'; // TRANSPARENCY TEXTURE
- ID_LTEX: TID4 = 'LTEX'; // LUMINANCE TEXTURE
- ID_BTEX: TID4 = 'BTEX'; // BUMP TEXTURE
- ID_TFLG: TID4 = 'TFLG'; // TEXTURE FLAGS
- ID_TSIZ: TID4 = 'TSIZ'; // TEXTURE SIZE
- ID_TCTR: TID4 = 'TCTR'; // TEXTURE CENTER
- ID_TFAL: TID4 = 'TFAL'; // TEXTURE FALLOFF
- ID_TVEL: TID4 = 'TVAL'; // TEXTURE VALUE
- ID_TREF: TID4 = 'TREF'; // TEXTURE REFERENCE
- ID_TCLR: TID4 = 'TCLR'; // TEXTURE COLOR
- ID_TVAL: TID4 = 'TVAL'; // TEXTURE VALUE
- ID_TAMP: TID4 = 'TAMP'; // TEXTURE AMPLITUDE
- ID_TFP0: TID4 = 'TFP0'; // TEXTURE PARAMETERS
- ID_TFP1: TID4 = 'TFP1'; //
- ID_TFP2: TID4 = 'TFP2'; //
- ID_TIP0: TID4 = 'TIP0'; //
- ID_TIP1: TID4 = 'TIP1'; //
- ID_TIP2: TID4 = 'TIP2'; //
- ID_TSP0: TID4 = 'TSP0'; //
- ID_TSP1: TID4 = 'TSP1'; //
- ID_TSP2: TID4 = 'TSP2'; //
- ID_TFRQ: TID4 = 'TFRQ'; //
- ID_TIMG: TID4 = 'TIMG'; // TEXTURE IMG
- ID_TALP: TID4 = 'TALP'; //
- ID_TWRP: TID4 = 'TWRP'; // TEXTURE WRAP
- ID_TAAS: TID4 = 'TAAS'; //
- ID_TOPC: TID4 = 'TOPC'; //
- ID_SHDR: TID4 = 'SHDR'; //
- ID_SDAT: TID4 = 'SDAT'; //
- ID_IMSQ: TID4 = 'IMSQ'; // IMAGE SEQUENCE
- ID_FLYR: TID4 = 'FLYR'; // FLYER SEQUENCE
- ID_IMCC: TID4 = 'IMCC'; //
- SURF_FLAG_LUMINOUS = 1;
- SURF_FLAG_OUTLINE = 2;
- SURF_FLAG_SMOOTHING = 4;
- SURF_FLAG_COLORHIGHLIGHTS = 8;
- SURF_FLAG_COLORFILTER = 16;
- SURF_FLAG_OPAQUEEDGE = 32;
- SURF_FLAG_TRANSPARENTEDGE = 64;
- SURF_FLAG_SHARPTERMINATOR = 128;
- SURF_FLAG_DOUBLESIDED = 256;
- SURF_FLAG_ADDITIVE = 512;
- SURF_FLAG_SHADOWALPHA = 1024;
- CURV_CONTINUITY_FIRST = 1;
- CURV_CONTINUITY_LAST = 2;
- IMSQ_FLAG_LOOP = 1;
- IMSQ_FLAG_INTERLACE = 2;
- ID_LWO2: TID4 = 'LWO2'; // OBJECT
- ID_VMAP: TID4 = 'VMAP'; // VERTEX MAP
- ID_TAGS: TID4 = 'TAGS'; // TAGS?
- ID_PTAG: TID4 = 'PTAG'; // POLYGON TAG MAP
- ID_VMAD: TID4 = 'VMAD'; // DISCONTINUOUS VERTEX MAP
- ID_ENVL: TID4 = 'ENVL'; // ENVELOPE
- ID_CLIP: TID4 = 'CLIP'; // CLIP
- ID_BBOX: TID4 = 'BBOX'; // BOUNDING BOX
- ID_DESC: TID4 = 'DESC'; // DESCRIPTION
- ID_TEXT: TID4 = 'TEXT'; // TEXT
- ID_ICON: TID4 = 'ICON'; // ICON
- ENVL_PRE: TID4 = 'PRE'#0; // PRE-BEHAVIOUR
- ENVL_POST: TID4 = 'POST'; // POST
- ENVL_KEY: TID4 = 'KEY'#0; // KEY
- ENVL_SPAN: TID4 = 'SPAN'; // SPAN
- ENVL_CHAN: TID4 = 'CHAN'; // CHAN
- ENVL_NAME: TID4 = 'NAME'; // NAME
- ID_STIL: TID4 = 'STIL'; // STILL IMAGE FILENAME
- ID_ISEQ: TID4 = 'ISEQ'; // IMAGE SEQUENCE
- ID_ANIM: TID4 = 'ANIM'; // PLUGIN ANIMATION
- ID_STCC: TID4 = 'STCC'; // COLOR CYCLING STILL
- ID_CONT: TID4 = 'CONT'; // CONTRAST
- ID_BRIT: TID4 = 'BRIT'; // BRIGHTNESS
- ID_SATR: TID4 = 'SATR'; // SATURATION
- ID_HUE: TID4 = 'HUE'#0; // HUE
- ID_GAMMA: TID4 = 'GAMM'; // GAMMA
- ID_NEGA: TID4 = 'NEGA'; // NEGATIVE IMAGE
- ID_IFLT: TID4 = 'IFLT'; // IMAGE PLUG-IN FILTER
- ID_PFLT: TID4 = 'PFLT'; // PIXEL PLUG-IN FILTER
- POLS_TYPE_FACE: TID4 = 'FACE'; // FACES
- POLS_TYPE_CURV: TID4 = 'CURV'; // CURVE
- POLS_TYPE_PTCH: TID4 = 'PTCH'; // PATCH
- POLS_TYPE_MBAL: TID4 = 'MBAL'; // METABALL
- POLS_TYPE_BONE: TID4 = 'BONE'; // SKELEGON?
- VMAP_TYPE_PICK: TID4 = 'PICK'; // SELECTION SET
- VMAP_TYPE_WGHT: TID4 = 'WGHT'; // WEIGHT MAP
- VMAP_TYPE_MNVW: TID4 = 'MNVW'; // SUBPATCH WEIGHT MAP
- VMAP_TYPE_TXUV: TID4 = 'TXUV'; // UV MAP
- VMAP_TYPE_RGB: TID4 = 'RGB'#0; // RGB MAP
- VMAP_TYPE_RGBA: TID4 = 'RGBA'; // RGBA MAP
- VMAP_TYPE_MORF: TID4 = 'MORF'; // MORPH MAP: RELATIVE VERTEX DISPLACEMENT
- VMAP_TYPE_SPOT: TID4 = 'SPOT'; // SPOT MAP: ABSOLUTE VERTEX POSITIONS
- PTAG_TYPE_SURF: TID4 = 'SURF'; // SURFACE
- PTAG_TYPE_PART: TID4 = 'PART'; // PARENT PART
- PTAG_TYPE_SMGP: TID4 = 'SMGP'; // SMOOTH GROUP
- PRE_POST_RESET = 0; // RESET
- PRE_POST_CONSTANT = 1; // CONSTANT
- PRE_POST_REPEAT = 2; // REPEAT
- PRE_POST_OSCILLATE = 3; // OSCILLATE
- PRE_POST_OFFSET = 4; // OFFSET REPEAT
- PRE_POST_LINEAR = 5; // LINEAR
- POLS_VCOUNT_MASK = $3FF;
- POLS_FLAGS_MASK = $FC00;
- SIDE_FRONT = 1;
- SIDE_BACK = 2;
- SIDE_FRONT_AND_BACK = SIDE_FRONT and SIDE_BACK;
- RFOP_BACKDROP = 0;
- RFOP_RAYTRACEANDBACKDROP = 1;
- RFOP_SPHERICALMAP = 2;
- RFOP_RAYTRACEANDSPHERICALMAP = 3;
- type
- TI1 = ShortInt;
- PI1 = ^TI1;
- TI2 = SmallInt;
- PI2 = ^TI2;
- TI4 = LongInt;
- PI4 = ^TI4;
- TU1 = Byte;
- PU1 = ^TU1;
- TU1DynArray = array of TU1;
- TU2 = Word;
- PU2 = ^TU2;
- TU2Array = array [0 .. 65534] of TU2;
- PU2Array = ^TU2Array;
- TU2DynArray = array of TU2;
- TU4 = LongWord;
- PU4 = ^TU4;
- TU4Array = array [0 .. 65534] of TU4;
- PU4Array = ^TU4Array;
- TU4DynArray = array of TU4;
- TF4 = Single;
- PF4 = ^TF4;
- TF4Array = array [0 .. 65534] of TF4;
- PF4Array = ^TF4Array;
- TF4DynArray = array of TF4;
- TANG4 = TF4;
- PANG4 = ^TANG4;
- // TS0 = PAnsiChar;
- TVec12 = array [0 .. 2] of TF4;
- PVec12 = ^TVec12;
- TVec12Array = array [0 .. 65534] of TVec12;
- PVec12Array = ^TVec12Array;
- TVec12DynArray = array of TVec12;
- TColr12 = TVec12;
- PColr12 = ^TColr12;
- TColr12DynArray = array of TColr12;
- TColr4 = array [0 .. 3] of TU1;
- PColr4 = ^TColr4;
- // Lightwave Chunk Struct - Used in TLWOReadCallback
- PLWChunkRec = ^TLWChunkRec;
- TLWChunkRec = record
- id: TID4;
- size: TU4;
- data: Pointer;
- end;
- // Lightwave SubChunk Struct - Used in TLWOReadCallback
- PLWSubChunkRec = ^TLWSubChunkRec;
- TLWSubChunkRec = record
- id: TID4;
- size: TU2;
- data: Pointer;
- end;
- TLWPolsInfo = record
- norm: TVec12;
- vnorms: TVec12DynArray;
- surfid: TU2;
- end;
- TLWPolsInfoDynArray = array of TLWPolsInfo;
- TLWPntsInfo = record
- npols: TU2;
- pols: TU2DynArray;
- end;
- TLWPntsInfoDynArray = array of TLWPntsInfo;
- TLWPolsDynArray = TU2DynArray;
- TLWPolyTagMapDynArray = TU2DynArray;
- TLWPolyTagMap = record
- poly: TU2;
- tag: TU2;
- end;
- PLWPolyTagMap = ^TLWPolyTagMap;
- // Value Map
- TLWVertexMap = record
- vert: TU2;
- values: TF4DynArray;
- end;
- TLWVertexMapDynArray = array of TLWVertexMap;
- TLWChunkList = class;
- TLWParentChunk = class;
- TLWChunk = class(TPersistent)
- private
- FData: Pointer;
- FID: TID4;
- FSize: TU4;
- FParentChunk: TLWParentChunk;
- FOwner: TLWChunkList;
- function GetRootChunks: TLWChunkList;
- function GetIndex: Integer;
- protected
- procedure Clear; virtual;
- procedure LoadData(AStream: TStream;
- DataStart, DataSize: LongWord); virtual;
- procedure Loaded; virtual;
- public
- destructor Destroy; override;
- class function GetID: TID4; virtual;
- procedure LoadFromStream(AStream: TStream); virtual;
- property data: Pointer read FData;
- property id: TID4 read FID;
- property size: TU4 read FSize;
- // ParentChunk may be nil indicating this is a root chunk. ie. TLWLayr
- property ParentChunk: TLWParentChunk read FParentChunk;
- property RootChunks: TLWChunkList read GetRootChunks;
- property Index: Integer read GetIndex;
- property Owner: TLWChunkList read FOwner;
- end;
- TLWChunkClass = class of TLWChunk;
- TLWSubChunk = class(TLWChunk)
- public
- procedure LoadFromStream(AStream: TStream); override;
- end;
- TLWChunkFind = procedure(AChunk: TLWChunk; Criteria: Pointer;
- var Found: boolean);
- TLWChunkList = class(TList)
- private
- FOwnsItems: boolean;
- FOwner: TObject;
- function GetItem(Index: Integer): TLWChunk;
- protected
- procedure Loaded; virtual;
- public
- constructor Create(AOwnsItems: boolean; AOwner: TObject);
- destructor Destroy; override;
- function Add(AChunk: TLWChunk): Integer;
- procedure Clear; override;
- procedure Delete(Index: Integer);
- function FindChunk(ChunkFind: TLWChunkFind; Criteria: Pointer;
- StartIndex: Integer = 0): Integer;
- property Items[Index: Integer]: TLWChunk read GetItem; default;
- property OwnsItems: boolean read FOwnsItems;
- property Owner: TObject read FOwner;
- end;
- TLWParentChunk = class(TLWChunk)
- private
- FItems: TLWChunkList;
- function GetItems: TLWChunkList;
- function GetFloatParam(Param: TID4): Single;
- function GetWordParam(Param: TID4): Word;
- function GetVec3Param(Param: TID4): TVec12;
- function GetLongParam(Param: TID4): LongWord;
- function GetVXParam(Param: TID4): Word;
- protected
- function GetParamAddr(Param: TID4): Pointer; virtual;
- procedure Clear; override;
- procedure Loaded; override;
- public
- property Items: TLWChunkList read GetItems;
- property ParamAddr[Param: TID4]: Pointer read GetParamAddr;
- property FloatParam[Param: TID4]: Single read GetFloatParam;
- property WordParam[Param: TID4]: Word read GetWordParam;
- property LongParam[Param: TID4]: LongWord read GetLongParam;
- property Vec3Param[Param: TID4]: TVec12 read GetVec3Param;
- property VXParam[Param: TID4]: Word read GetVXParam;
- end;
- TLWVMap = class;
- TLWPnts = class(TLWParentChunk)
- private
- FPnts: TVec12DynArray;
- FPntsInfo: TLWPntsInfoDynArray;
- function GetPntsCount: LongWord;
- function AddPoly(PntIdx, PolyIdx: Integer): Integer;
- protected
- procedure Clear; override;
- procedure LoadData(AStream: TStream;
- DataStart, DataSize: LongWord); override;
- public
- class function GetID: TID4; override;
- function GetVMap(VMapID: TID4; out VMap: TLWVMap): boolean;
- property PntsCount: LongWord read GetPntsCount;
- property Pnts: TVec12DynArray read FPnts;
- property PntsInfo: TLWPntsInfoDynArray read FPntsInfo;
- end;
- TLWPols = class(TLWParentChunk)
- private
- FPolsType: TID4;
- FPols: TLWPolsDynArray;
- FPolsInfo: TLWPolsInfoDynArray;
- FPolsCount: Integer;
- function GetPolsByIndex(AIndex: TU2): Integer;
- function GetIndiceCount: TU4;
- function GetIndice(AIndex: Integer): TU2;
- function GetPolsCount: Integer;
- procedure CalcPolsNormals;
- procedure CalcPntsNormals;
- protected
- procedure Clear; override;
- procedure LoadData(AStream: TStream;
- DataStart, DataSize: LongWord); override;
- procedure Loaded; override;
- public
- class function GetID: TID4; override;
- function GetPolsByPntIdx(VertIdx: TU2; var VertPolys: TU2DynArray): Integer;
- property PolsByIndex[AIndex: TU2]: Integer read GetPolsByIndex;
- property IndiceCount: TU4 read GetIndiceCount;
- property Indices[AIndex: Integer]: TU2 read GetIndice;
- property PolsType: TID4 read FPolsType;
- property PolsCount: Integer read GetPolsCount;
- property PolsInfo: TLWPolsInfoDynArray read FPolsInfo;
- end;
- TLWVMap = class(TLWChunk)
- private
- FDimensions: TU2;
- FName: string;
- FValues: TLWVertexMapDynArray;
- FVMapType: TID4;
- function GetValue(AIndex: TU2): TLWVertexMap;
- function GetValueCount: Integer;
- protected
- procedure Clear; override;
- procedure LoadData(AStream: TStream;
- DataStart, DataSize: LongWord); override;
- public
- class function GetID: TID4; override;
- property Dimensions: TU2 read FDimensions;
- property Name: string read FName;
- property Value[AIndex: TU2]: TLWVertexMap read GetValue;
- property ValueCount: Integer read GetValueCount;
- property VMapType: TID4 read FVMapType;
- end;
- TLWTags = class(TLWChunk)
- private
- FTags: TStrings;
- function GetTags: TStrings;
- protected
- procedure Clear; override;
- procedure LoadData(AStream: TStream;
- DataStart, DataSize: LongWord); override;
- public
- destructor Destroy; override;
- class function GetID: TID4; override;
- function TagToName(tag: TU2): string;
- property Tags: TStrings read GetTags;
- end;
- TLWSurf = class(TLWParentChunk)
- private
- FName: string;
- FSource: string;
- function GetSurfId: Integer;
- protected
- function GetParamAddr(Param: TID4): Pointer; override;
- procedure LoadData(AStream: TStream;
- DataStart, DataSize: LongWord); override;
- public
- destructor Destroy; override;
- class function GetID: TID4; override;
- property surfid: Integer read GetSurfId;
- property Name: string read FName;
- property Source: string read FSource;
- end;
- TLWLayr = class(TLWParentChunk)
- private
- FFlags: TU2;
- FName: string;
- FNumber: TU2;
- FParent: TU2;
- FPivot: TVec12;
- protected
- procedure LoadData(AStream: TStream;
- DataStart, DataSize: LongWord); override;
- public
- destructor Destroy; override;
- class function GetID: TID4; override;
- property Flags: TU2 read FFlags;
- property Name: string read FName;
- property Number: TU2 read FNumber;
- property Parent: TU2 read FParent;
- property Pivot: TVec12 read FPivot;
- end;
- TLWPTag = class(TLWChunk)
- private
- FMapType: TID4;
- FTagMaps: TLWPolyTagMapDynArray;
- FTags: TU2DynArray;
- function AddTag(Value: TU2): Integer;
- function GetTag(AIndex: Integer): TU2;
- function GetTagCount: Integer;
- function GetTagMapCount: Integer;
- function GetTagMaps(AIndex: Integer): TLWPolyTagMap;
- procedure ValidateTagInfo;
- protected
- procedure Clear; override;
- procedure LoadData(AStream: TStream;
- DataStart, DataSize: LongWord); override;
- public
- constructor Create;
- function GetPolsByTag(tag: TU2; var PolyIndices: TU2DynArray): Integer;
- class function GetID: TID4; override;
- property MapType: TID4 read FMapType;
- property TagCount: Integer read GetTagCount;
- property TagMapCount: Integer read GetTagMapCount;
- property TagMaps[AIndex: Integer]: TLWPolyTagMap read GetTagMaps; default;
- property Tags[AIndex: Integer]: TU2 read GetTag;
- end;
- TLWObjectFile = class(TObject)
- private
- FChunks: TLWChunkList;
- FFileName: string;
- function GetChunks: TLWChunkList;
- function GetCount: Integer;
- function GetSurfaceByName(Index: string): TLWSurf;
- function GetSurfaceByTag(Index: TU2): TLWSurf;
- public
- constructor Create;
- destructor Destroy; override;
- function TagToName(tag: TU2): string;
- procedure LoadFromFile(const AFilename: string);
- procedure LoadFromStream(AStream: TStream);
- property ChunkCount: Integer read GetCount;
- property Chunks: TLWChunkList read GetChunks;
- property FileName: string read FFileName;
- property SurfaceByName[Index: string]: TLWSurf read GetSurfaceByName;
- property SurfaceByTag[Index: TU2]: TLWSurf read GetSurfaceByTag;
- end;
- TLWClip = class(TLWParentChunk)
- private
- FClipIndex: TU4;
- protected
- procedure LoadData(AStream: TStream;
- DataStart, DataSize: LongWord); override;
- public
- class function GetID: TID4; override;
- property ClipIndex: TU4 read FClipIndex;
- end;
- TLWContentNotify = procedure(Sender: TObject; var Content: string) of object;
- TLWContentDir = class
- private
- FSubDirs: TStrings;
- FRoot: string;
- function GetSubDirs: TStrings;
- procedure SetRoot(const Value: string);
- procedure SetSubDirs(const Value: TStrings);
- // function ContentSearch(AFilename: string): string;
- public
- destructor Destroy; override;
- function FindContent(AFilename: string): string;
- property Root: string read FRoot write SetRoot;
- property SubDirs: TStrings read GetSubDirs write SetSubDirs;
- end;
- TLWOReadCallback = procedure(Chunk: TLWChunkRec; data: Pointer); cdecl;
- procedure RegisterChunkClass(ChunkClass: TLWChunkClass);
- function LoadLW0FromStream(Stream: TStream; ReadCallback: TLWOReadCallback;
- UserData: Pointer): LongWord; cdecl;
- function LoadLWOFromFile(const AFilename: string;
- ReadCallback: TLWOReadCallback; UserData: Pointer): LongWord;
- procedure ReadMotorolaNumber(Stream: TStream; data: Pointer;
- ElementSize: Integer; Count: Integer = 1);
- function WriteMotorolaNumber(Stream: TStream; data: Pointer;
- ElementSize: Integer; Count: Integer = 1): Integer;
- function ReadS0(Stream: TStream; out Str: string): Integer;
- procedure WriteS0(Stream: TStream; data: string);
- procedure WriteU4AsVX(Stream: TStream; data: Pointer; Count: Integer);
- function ReadVXAsU4(Stream: TStream; data: Pointer; Count: Integer = 1)
- : Integer;
- procedure ReverseByteOrder(ValueIn: Pointer; size: Integer; Count: Integer = 1);
- function ToDosPath(const Path: string): string;
- function ToUnixPath(const Path: string): string;
- function ID4ToInt(const id: TID4): Integer;
- // ChunkFind procedures
- procedure FindChunkById(AChunk: TLWChunk; data: Pointer; var Found: boolean);
- procedure FindSurfaceByName(AChunk: TLWChunk; AName: Pointer;
- var Found: boolean);
- procedure FindSurfaceByTag(AChunk: TLWChunk; ATag: Pointer; var Found: boolean);
- procedure FindVMapByName(AChunk: TLWChunk; AName: Pointer; var Found: boolean);
- procedure FindClipByClipIndex(AChunk: TLWChunk; AIndex: Pointer;
- var Found: boolean);
- function GetContentDir: TLWContentDir;
- // --------------------------------------------------------------------
- implementation
- // --------------------------------------------------------------------
- type
- PWord = ^Word;
- PLongWord = ^LongWord;
- var
- ChunkClasses: TList;
- ContentDir: TLWContentDir;
- function ToDosPath(const Path: string): string;
- var
- i: Integer;
- begin
- result := Path;
- for i := 1 to Length(result) do
- if result[i] = '/' then
- result[i] := '\';
- end;
- function ToUnixPath(const Path: string): string;
- var
- i: Integer;
- begin
- result := Path;
- for i := 1 to Length(result) do
- if result[i] = '\' then
- result[i] := '/';
- end;
- function GetContentDir: TLWContentDir;
- begin
- if ContentDir = nil then
- ContentDir := TLWContentDir.Create;
- result := ContentDir;
- end;
- procedure FindChunkById(AChunk: TLWChunk; data: Pointer; var Found: boolean);
- begin
- if AChunk.FID = PID4(data)^ then
- Found := true
- else
- Found := false;
- end;
- procedure FindClipByClipIndex(AChunk: TLWChunk; AIndex: Pointer;
- var Found: boolean);
- begin
- if (AChunk is TLWClip) and (TLWClip(AChunk).ClipIndex = PU2(AIndex)^) then
- Found := true;
- end;
- procedure FindSurfaceByName(AChunk: TLWChunk; AName: Pointer;
- var Found: boolean);
- begin
- if (AChunk is TLWSurf) and (TLWSurf(AChunk).Name = PString(AName)^) then
- Found := true;
- end;
- procedure FindSurfaceByTag(AChunk: TLWChunk; ATag: Pointer; var Found: boolean);
- begin
- if (AChunk is TLWSurf) and (TLWSurf(AChunk).surfid = PU2(ATag)^) then
- Found := true;
- end;
- procedure FindVMapByName(AChunk: TLWChunk; AName: Pointer; var Found: boolean);
- begin
- if (AChunk is TLWVMap) and (TLWVMap(AChunk).Name = PString(AName)^) then
- Found := true;
- end;
- function VecAdd(v1, v2: TVec12): TVec12;
- begin
- result[0] := v1[0] + v2[0];
- result[1] := v1[1] + v2[1];
- result[2] := v1[2] + v2[2];
- end;
- function VecSub(v1, v2: TVec12): TVec12;
- begin
- result[0] := v1[0] - v2[0];
- result[1] := v1[1] - v2[1];
- result[2] := v1[2] - v2[2];
- end;
- function VecCross(v1, v2: TVec12): TVec12;
- begin
- result[0] := v1[1] * v2[2] - v1[2] * v2[1];
- result[1] := v1[2] * v2[0] - v1[0] * v2[2];
- result[2] := v1[0] * v2[1] - v1[1] * v2[0];
- end;
- function VecDot(v1, v2: TVec12): TF4;
- begin
- result := v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
- end;
- function VecNorm(v: TVec12): TVec12;
- var
- mag: TF4;
- begin
- mag := Sqrt(VecDot(v, v));
- if mag > 0 then
- mag := 1 / mag;
- result[0] := v[0] * mag;
- result[1] := v[1] * mag;
- result[2] := v[2] * mag;
- end;
- function CalcPlaneNormal(v1, v2, v3: TVec12): TVec12;
- var
- e1, e2: TVec12;
- begin
- e1 := VecSub(v2, v1);
- e2 := VecSub(v3, v1);
- result := VecCross(e1, e2);
- result := VecNorm(result);
- end;
- procedure FindSurfByName(Chunk: TLWChunk; var Found: boolean);
- begin
- end;
- (*-----------------------------------------------------------------------------
- Procedure: GetChunkClasses
- Date: 08-Aug-2002
- Arguments: None
- Result: TClassList
- Singleton access for the chunk class list.
- -----------------------------------------------------------------------------*)
- function GetChunkClasses: TList;
- begin
- if ChunkClasses = nil then
- ChunkClasses := TList.Create;
- result := ChunkClasses;
- end;
- procedure UnRegisterChunkClasses;
- var
- i: Integer;
- begin
- with GetChunkClasses do
- for i := 0 to Count - 1 do
- UnregisterClass(TPersistentClass(Items[i]));
- end;
- (*-----------------------------------------------------------------------------
- Procedure: RegisterChunkClass
- Date: 08-Aug-2002
- Arguments: ChunkClass: TLWChunkClass
- Result: None
- Adds a user defined chunk class to the chunk class list.
- -----------------------------------------------------------------------------*)
- procedure RegisterChunkClass(ChunkClass: TLWChunkClass);
- begin
- GetChunkClasses.Add(ChunkClass);
- // if FindClass(ChunkClass.ClassName) <> nil then
- // UnRegisterClass(ChunkClass);
- // RegisterClass(ChunkClass);
- end;
- (*-----------------------------------------------------------------------------
- Procedure: GetChunkClass
- Date: 08-Aug-2002
- Arguments: ChunkID: TID4
- Result: TLWChunkClass
- Returns the chunk class associated with ChunkID.
- -----------------------------------------------------------------------------*)
- function GetChunkClass(ChunkID: TID4; ADefault: TLWChunkClass): TLWChunkClass;
- var
- i: Integer;
- begin
- if ADefault = nil then
- result := TLWChunk
- else
- result := ADefault;
- for i := 0 to ChunkClasses.Count - 1 do
- begin
- if TLWChunkClass(ChunkClasses.Items[i]).GetID = ChunkID then
- begin
- result := TLWChunkClass(ChunkClasses.Items[i]);
- Exit;
- end;
- end;
- end;
- (*-----------------------------------------------------------------------------
- Procedure: Tokenize
- Date: 08-Aug-2002
- Arguments: const Src: string; Delimiter: Char; Dst: TStrings
- Result: None
- Breaks up a string into TStrings items when the Delimiter character is
- encountered.
- -----------------------------------------------------------------------------*)
- procedure Tokenize(const Src: string; Delimiter: Char; Dst: TStrings);
- var
- i, L, SL: Integer;
- SubStr: string;
- begin
- if Dst = nil then
- Exit;
- L := Length(Src);
- if (L = 0) or (Dst = nil) then
- Exit;
- SubStr := '';
- for i := 1 to L do
- begin
- if (Src[i] <> Delimiter) then
- SubStr := SubStr + Src[i]
- else
- begin
- SL := Length(SubStr);
- if SL > 0 then
- begin
- Dst.Add(SubStr);
- SubStr := '';
- end;
- end;
- end;
- if Length(SubStr) > 0 then
- Dst.Add(SubStr);
- end;
- (*-----------------------------------------------------------------------------
- Procedure: LoadLW0FromStream
- Date: 08-Aug-2002
- Arguments: Stream: TStream; ReadCallback: TLWOReadCallback; UserData: Pointer
- Result: LongWord
- -----------------------------------------------------------------------------*)
- function LoadLW0FromStream(Stream: TStream; ReadCallback: TLWOReadCallback;
- UserData: Pointer): LongWord;
- var
- Chunk: TLWChunkRec;
- CurId: TID4;
- StartPos, CurSize: TU4;
- begin
- try
- Stream.Read(CurId, 4);
- ReadMotorolaNumber(Stream, @CurSize, 4);
- if UpperCase(string(CurId)) = 'FORM' then
- begin
- Stream.Read(CurId, 4);
- end
- else
- raise Exception.Create
- ('Invalid magic number. Not a valid Lightwave Object');
- with Stream do
- while Position < size do
- begin
- Read(Chunk, 8);
- ReverseByteOrder(@Chunk.size, 4);
- StartPos := Position;
- GetMem(Chunk.data, Chunk.size);
- Stream.Read(Chunk.data^, Chunk.size);
- if Assigned(ReadCallback) then
- ReadCallback(Chunk, UserData);
- FreeMem(Chunk.data, Chunk.size);
- Position := StartPos + Chunk.size + (StartPos + Chunk.size) mod 2;
- end;
- Stream.Free;
- result := High(LongWord);
- except
- On E: Exception do
- begin
- Stream.Free;
- result := 0;
- end;
- end;
- end;
- function LoadLWOFromFile(const AFilename: String;
- ReadCallback: TLWOReadCallback; UserData: Pointer): LongWord;
- var
- Stream: TStream;
- begin
- Stream := TFileStream.Create(AFilename, fmOpenRead);
- try
- result := LoadLW0FromStream(Stream, ReadCallback, UserData);
- finally
- Stream.Free;
- end;
- end;
- procedure ReverseByteOrder(ValueIn: Pointer; size: Integer; Count: Integer = 1);
- var
- W: Word;
- pB: PByte;
- Blo, Bhi: Byte;
- L: LongWord;
- i: Integer;
- begin
- i := 0;
- case size of
- 2:
- begin
- while i < Count do
- begin
- W := PU2Array(ValueIn)^[i];
- pB := @W;
- Blo := pB^;
- Inc(pB);
- Bhi := pB^;
- pB^ := Blo;
- Dec(pB);
- pB^ := Bhi;
- PU2Array(ValueIn)^[i] := W;
- Inc(i);
- end;
- end;
- 4:
- begin
- while i < Count do
- begin
- L := PU4Array(ValueIn)^[i];
- pB := @W;
- Blo := pB^;
- Inc(pB);
- Bhi := pB^;
- pB^ := Blo;
- Dec(pB);
- pB^ := Bhi;
- PU4Array(ValueIn)^[i] := L;
- Inc(i);
- end;
- end;
- else
- raise Exception.Create('Lightwave.ReverseByteOrder: Invalid Size = ' +
- IntToStr(size));
- end;
- end;
- procedure ReadMotorolaNumber(Stream: TStream; data: Pointer;
- ElementSize: Integer; Count: Integer = 1);
- begin
- Stream.Read(data^, Count * ElementSize);
- if (ElementSize = 2) or (ElementSize = 4) then
- ReverseByteOrder(data, ElementSize, Count);
- end;
- function WriteMotorolaNumber(Stream: TStream; data: Pointer;
- ElementSize: Integer; Count: Integer = 1): Integer;
- var
- TempData: Pointer;
- begin
- result := 0;
- if data <> nil then
- begin
- TempData := AllocMem(ElementSize * Count);
- try
- if (ElementSize = 2) or (ElementSize = 4) then
- ReverseByteOrder(TempData, ElementSize, Count);
- result := Stream.Write(data, Count * ElementSize);
- except
- on E: Exception do
- begin
- FreeMem(TempData, Count * ElementSize);
- raise;
- end;
- end;
- end;
- end;
- function ReadS0(Stream: TStream; out Str: string): Integer;
- var
- Buf: array [0 .. 1] of AnsiChar;
- StrBuf: string;
- begin
- Stream.Read(Buf, 2);
- StrBuf := '';
- while Buf[1] <> #0 do
- begin
- StrBuf := StrBuf + string(Buf);
- Stream.Read(Buf, 2);
- end;
- if Buf[0] <> #0 then
- StrBuf := StrBuf + Char(Buf[0]);
- Str := Copy(StrBuf, 1, Length(StrBuf));
- result := Length(Str) + 1;
- result := result + (result mod 2);
- end;
- function ValueOfVX(VX: Pointer): TU4;
- var
- TmpU2: TU2;
- TmpU4: TU4;
- begin
- if PU1(VX)^ = $FF then
- begin
- TmpU4 := TU4(PU1(VX)^) and $FFFFFFF0;
- ReverseByteOrder(@TmpU4, 4);
- end
- else
- begin
- TmpU2 := TU2(PU2(VX)^);
- ReverseByteOrder(@TmpU2, 2);
- TmpU4 := TmpU2;
- end;
- result := TmpU4;
- end;
- function ReadVXAsU4(Stream: TStream; data: Pointer; Count: Integer = 1)
- : Integer;
- var
- i, ReadCount: Integer;
- BufByte: Byte;
- TempU2: TU2;
- begin
- ReadCount := 0;
- for i := 0 to Count - 1 do
- begin
- Stream.Read(BufByte, 1);
- Stream.Position := Stream.Position - 1;
- if BufByte = 255 then
- begin
- Stream.Read(data^, SizeOf(TU4));
- PU4Array(data)^[i] := PU4Array(data)^[i] and $FFFFFFF0;
- ReverseByteOrder(data, SizeOf(TU4));
- Inc(ReadCount, 4);
- end
- else
- begin
- Stream.Read(TempU2, SizeOf(TU2));
- ReverseByteOrder(@TempU2, SizeOf(TU2));
- PU4Array(data)^[i] := TempU2;
- Inc(ReadCount, 2);
- end;
- end;
- result := ReadCount;
- end;
- function ReadVXAsU2(Stream: TStream; data: Pointer; Count: Integer = 1)
- : Integer;
- var
- i, ReadCount: Integer;
- BufByte: Byte;
- TempU2: TU2;
- begin
- ReadCount := 0;
- for i := 0 to Count - 1 do
- begin
- Stream.Read(BufByte, 1);
- Stream.Position := Stream.Position - 1;
- if BufByte = 255 then
- begin
- Stream.Position := Stream.Position + 4;
- PU2Array(data)^[i] := 0;
- Inc(ReadCount, 4);
- end
- else
- begin
- Stream.Read(TempU2, SizeOf(TU2));
- ReverseByteOrder(@TempU2, SizeOf(TU2));
- PU2Array(data)^[i] := TempU2;
- Inc(ReadCount, 2);
- end;
- end;
- result := ReadCount;
- end;
- procedure WriteS0(Stream: TStream; data: string);
- begin
- // ToDo: WriteS0
- end;
- procedure WriteU4AsVX(Stream: TStream; data: Pointer; Count: Integer);
- var
- i: Integer;
- TempU2: TU2;
- begin
- for i := 0 to Count - 1 do
- begin
- if PU4Array(data)^[i] < 65280 then
- begin
- TempU2 := PU4Array(data)^[i];
- WriteMotorolaNumber(Stream, @TempU2, SizeOf(TU2));
- end
- else
- WriteMotorolaNumber(Stream, data, SizeOf(TU4));
- end;
- end;
- type
- PInteger = ^Integer;
- function ID4ToInt(const id: TID4): Integer;
- var
- TmpId: AnsiString;
- begin
- TmpId := id;
- TmpId := AnsiString(UpperCase(string(id)));
- result := PInteger(@TmpId)^;
- end;
- (*********************************** TLWChunk ********************************)
- destructor TLWChunk.Destroy;
- begin
- Clear;
- inherited;
- end;
- procedure TLWChunk.Clear;
- begin
- FreeMem(FData, FSize);
- FSize := 0;
- FData := nil;
- end;
- class function TLWChunk.GetID: TID4;
- begin
- result := #0#0#0#0;
- end;
- procedure TLWChunk.LoadData(AStream: TStream; DataStart, DataSize: LongWord);
- begin
- GetMem(FData, DataSize);
- AStream.Read(PByteArray(FData)^[0], DataSize);
- end;
- procedure TLWChunk.LoadFromStream(AStream: TStream);
- var
- DataStart: Integer;
- DataSize: TU4;
- begin
- with AStream do
- begin
- ReadMotorolaNumber(AStream, @DataSize, 4);
- DataStart := Position;
- FSize := DataSize;
- LoadData(AStream, DataStart, DataSize);
- Position := Cardinal(DataStart) + DataSize +
- (Cardinal(DataStart) + DataSize) mod 2;
- end;
- end;
- (********************************* TLWChunkList *******************************)
- constructor TLWChunkList.Create(AOwnsItems: boolean; AOwner: TObject);
- begin
- inherited Create;
- FOwnsItems := AOwnsItems;
- FOwner := AOwner;
- end;
- destructor TLWChunkList.Destroy;
- begin
- Clear;
- inherited;
- end;
- procedure TLWChunkList.Clear;
- begin
- while Count > 0 do
- Delete(Count - 1);
- inherited;
- end;
- procedure TLWChunkList.Delete(Index: Integer);
- begin
- if FOwnsItems then
- Items[Index].Free;
- inherited Delete(Index);
- end;
- function TLWChunkList.GetItem(Index: Integer): TLWChunk;
- begin
- result := TLWChunk(inherited Items[Index]);
- end;
- (******************************** TLWObjectFile *******************************)
- constructor TLWObjectFile.Create;
- begin
- inherited;
- end;
- destructor TLWObjectFile.Destroy;
- begin
- FreeAndNil(FChunks);
- inherited;
- end;
- function TLWObjectFile.GetChunks: TLWChunkList;
- begin
- if FChunks = nil then
- FChunks := TLWChunkList.Create(true, Self);
- result := FChunks;
- end;
- function TLWObjectFile.GetCount: Integer;
- begin
- result := Chunks.Count;
- end;
- function TLWObjectFile.GetSurfaceByName(Index: string): TLWSurf;
- var
- SurfIdx: Integer;
- begin
- SurfIdx := Chunks.FindChunk(@FindSurfaceByName, @Index, 0);
- if SurfIdx <> -1 then
- result := TLWSurf(Chunks[SurfIdx])
- else
- result := nil;
- end;
- function TLWObjectFile.GetSurfaceByTag(Index: TU2): TLWSurf;
- var
- TagName: string;
- begin
- TagName := TagToName(Index);
- result := SurfaceByName[TagName];
- end;
- procedure TLWObjectFile.LoadFromFile(const AFilename: string);
- var
- Stream: TMemoryStream;
- begin
- Stream := TMemoryStream.Create;
- try
- Stream.LoadFromFile(AFilename);
- LoadFromStream(Stream);
- Stream.Free;
- FFileName := AFilename;
- except
- on E: Exception do
- begin
- Stream.Free;
- raise;
- end;
- end;
- end;
- procedure TLWObjectFile.LoadFromStream(AStream: TStream);
- var
- CurId: TID4;
- CurSize: LongWord;
- CurPnts, CurPols, CurItems: TLWChunkList;
- begin
- CurPols := nil;
- CurPnts := nil;
- AStream.Read(CurId, 4);
- ReadMotorolaNumber(AStream, @CurSize, 4);
- if UpperCase(string(CurId)) = 'FORM' then
- begin
- AStream.Read(CurId, 4);
- if CurId <> 'LWO2' then
- raise Exception.Create
- ('Only Version 6.0+ version objects are supported.');
- end
- else
- raise Exception.Create
- ('Invalid magic number. Not a valid Lightwave Object');
- CurItems := Chunks;
- while AStream.Position < AStream.size do
- begin
- AStream.Read(CurId, 4);
- if (CurId = ID_PTAG) then
- begin
- CurPols.Add(GetChunkClass(CurId, TLWChunk).Create);
- with CurPols[CurPols.Count - 1] do
- begin
- FID := CurId;
- LoadFromStream(AStream);
- end;
- end
- else if (CurId = ID_VMAP) or (CurId = ID_VMAD) then
- begin
- CurPnts.Add(GetChunkClass(CurId, TLWChunk).Create);
- with CurPnts[CurPnts.Count - 1] do
- begin
- FID := CurId;
- LoadFromStream(AStream);
- end;
- end
- else
- begin
- if (CurId = ID_LAYR) or (CurId = ID_SURF) or (CurId = ID_TAGS) or
- (CurId = ID_CLIP) then
- CurItems := Chunks;
- CurItems.Add(GetChunkClass(CurId, TLWChunk).Create);
- with CurItems[CurItems.Count - 1] do
- begin
- FID := CurId;
- LoadFromStream(AStream);
- end;
- end;
- if CurId = ID_LAYR then
- CurItems := TLWParentChunk(CurItems[CurItems.Count - 1]).Items
- else if CurId = ID_POLS then
- CurPols := TLWParentChunk(CurItems[CurItems.Count - 1]).Items
- else if CurId = ID_PNTS then
- CurPnts := TLWParentChunk(CurItems[CurItems.Count - 1]).Items;
- end;
- Chunks.Loaded;
- end;
- (*********************************** TLWPnts **********************************)
- function TLWPnts.AddPoly(PntIdx, PolyIdx: Integer): Integer;
- var
- i, L: Integer;
- begin
- // DONE: Pnts.AddPoly
- for i := 0 to FPntsInfo[PntIdx].npols - 1 do
- begin
- if FPntsInfo[PntIdx].pols[i] = PolyIdx then
- begin
- result := i;
- Exit;
- end;
- end;
- L := Length(FPntsInfo[PntIdx].pols);
- SetLength(FPntsInfo[PntIdx].pols, L + 1);
- FPntsInfo[PntIdx].npols := L + 1;
- FPntsInfo[PntIdx].pols[L] := PolyIdx;
- result := L;
- end;
- procedure TLWPnts.Clear;
- var
- i: Integer;
- begin
- for i := 0 to PntsCount - 1 do
- SetLength(FPntsInfo[i].pols, 0);
- SetLength(FPntsInfo, 0);
- SetLength(FPnts, 0);
- end;
- function TLWPnts.GetPntsCount: LongWord;
- begin
- result := Length(FPnts);
- end;
- class function TLWPnts.GetID: TID4;
- begin
- result := ID_PNTS;
- end;
- function TLWPnts.GetVMap(VMapID: TID4; out VMap: TLWVMap): boolean;
- var
- i: Integer;
- begin
- result := false;
- for i := 0 to Items.Count - 1 do
- begin
- if (Items[i] is TLWVMap) and (TLWVMap(Items[i]).VMapType = VMapID) then
- begin
- result := true;
- VMap := TLWVMap(Items[i]);
- Exit;
- end;
- end;
- end;
- procedure TLWPnts.LoadData(AStream: TStream; DataStart, DataSize: LongWord);
- begin
- SetLength(FPnts, DataSize div 12);
- // allocate storage for DataSize div 12 points
- SetLength(FPntsInfo, DataSize div 12); // Point info
- ReadMotorolaNumber(AStream, @FPnts[0], 4, DataSize div 4);
- // read the point data
- end;
- (*********************************** TLWPols **********************************)
- procedure TLWPols.CalcPolsNormals;
- var
- i, j, PolyIdx: Integer;
- Pnts: TLWPnts;
- begin
- if IndiceCount = 0 then
- Exit;
- with ParentChunk as TLWLayr do
- Pnts := TLWPnts(Items[Items.FindChunk(@FindChunkById, @ID_PNTS, 0)]);
- for PolyIdx := 0 to FPolsCount - 1 do
- begin
- // DONE: call Pnts.AddPoly
- i := PolsByIndex[PolyIdx];
- with Pnts do
- begin
- for j := 1 to Indices[i] do
- AddPoly(Indices[i + j], PolyIdx);
- SetLength(FPolsInfo[PolyIdx].vnorms, Indices[i]);
- if Indices[PolyIdx] > 2 then
- FPolsInfo[PolyIdx].norm := CalcPlaneNormal(Pnts[Indices[i + 1]],
- Pnts[Indices[i + 2]], Pnts[Indices[i + 3]])
- else
- FPolsInfo[PolyIdx].norm := VecNorm(Pnts[Indices[i + 1]]);
- end;
- end;
- end;
- procedure TLWPols.Clear;
- var
- i: Integer;
- begin
- for i := 0 to FPolsCount - 1 do
- SetLength(FPolsInfo[i].vnorms, 0);
- SetLength(FPolsInfo, 0);
- SetLength(FPols, 0);
- end;
- function TLWPols.GetPolsByIndex(AIndex: TU2): Integer;
- var
- i, cnt: Cardinal;
- begin
- result := -1;
- i := 0;
- cnt := 0;
- if AIndex = 0 then
- begin
- result := 0;
- Exit;
- end;
- while (i < IndiceCount - 1) and (cnt <> AIndex) do
- begin
- Inc(i, Indices[i] + 1);
- Inc(cnt);
- end;
- if cnt = AIndex then
- result := i;
- end;
- class function TLWPols.GetID: TID4;
- begin
- result := ID_POLS;
- end;
- function TLWPols.GetIndiceCount: TU4;
- begin
- result := Length(FPols);
- end;
- function TLWPols.GetIndice(AIndex: Integer): TU2;
- begin
- result := FPols[AIndex];
- end;
- function TLWPols.GetPolsCount: Integer;
- begin
- result := FPolsCount;
- end;
- procedure TLWPols.LoadData(AStream: TStream; DataStart, DataSize: LongWord);
- var
- EndPos: Integer;
- Idx: TU4;
- TmpU2: TU2;
- begin
- Idx := 0;
- EndPos := DataStart + DataSize;
- with AStream do
- begin
- Read(FPolsType, 4);
- // To avoid memory manager hits, set an estimate length of indices
- SetLength(FPols, (DataSize - 4) div 2);
- while Position < EndPos do
- begin
- ReadMotorolaNumber(AStream, @FPols[Idx], 2);
- TmpU2 := FPols[Idx] and POLS_VCOUNT_MASK;
- ReadVXAsU2(AStream, @FPols[Idx + 1], TmpU2);
- Inc(Idx, FPols[Idx] + 1);
- Inc(FPolsCount);
- end;
- // correct length estimate errors if any
- if (Idx + 1) < Cardinal(Length(FPols)) then
- SetLength(FPols, Idx + 1);
- end;
- SetLength(FPolsInfo, FPolsCount);
- CalcPolsNormals;
- end;
- (*********************************** TLWVMap **********************************)
- procedure TLWVMap.Clear;
- var
- i: Integer;
- begin
- for i := 0 to Length(FValues) - 1 do
- SetLength(FValues[i].values, 0);
- SetLength(FValues, 0);
- end;
- class function TLWVMap.GetID: TID4;
- begin
- result := ID_VMAP;
- end;
- function TLWVMap.GetValue(AIndex: TU2): TLWVertexMap;
- begin
- result := FValues[AIndex];
- end;
- function TLWVMap.GetValueCount: Integer;
- begin
- result := Length(FValues);
- end;
- procedure TLWVMap.LoadData(AStream: TStream; DataStart, DataSize: LongWord);
- var
- Idx: TU4;
- begin
- Idx := 0;
- with AStream do
- begin
- Read(FVMapType, 4);
- ReadMotorolaNumber(AStream, @FDimensions, 2);
- ReadS0(AStream, FName);
- if FDimensions > 0 then
- begin
- while Cardinal(Position) < (DataStart + DataSize) do
- begin
- SetLength(FValues, Length(FValues) + 1);
- ReadVXAsU2(AStream, @FValues[Idx].vert, 1);
- SetLength(FValues[Idx].values, Dimensions * 4);
- ReadMotorolaNumber(AStream, @FValues[Idx].values[0], 4, Dimensions);
- Inc(Idx);
- end;
- end;
- end;
- end;
- (*********************************** TLWTags **********************************)
- destructor TLWTags.Destroy;
- begin
- inherited;
- end;
- procedure TLWTags.Clear;
- begin
- FreeAndNil(FTags);
- end;
- class function TLWTags.GetID: TID4;
- begin
- result := ID_TAGS;
- end;
- function TLWTags.GetTags: TStrings;
- begin
- if FTags = nil then
- FTags := TStringList.Create;
- result := FTags;
- end;
- procedure TLWTags.LoadData(AStream: TStream; DataStart, DataSize: LongWord);
- var
- EndPos: TU4;
- TmpStr: string;
- begin
- EndPos := DataStart + DataSize;
- while Cardinal(AStream.Position) < Cardinal(EndPos) do
- begin
- ReadS0(AStream, TmpStr);
- Tags.Add(TmpStr);
- TmpStr := '';
- end;
- end;
- function TLWTags.TagToName(tag: TU2): string;
- begin
- result := Tags[tag];
- end;
- (********************************* TLWSubChunk ********************************)
- procedure TLWSubChunk.LoadFromStream(AStream: TStream);
- var
- DataStart: Integer;
- DataSize: TU2;
- begin
- with AStream do
- begin
- ReadMotorolaNumber(AStream, @DataSize, 2);
- DataStart := Position;
- FSize := DataSize;
- LoadData(AStream, DataStart, DataSize);
- Position := DataStart + DataSize + (DataStart + DataSize) mod 2;
- end;
- end;
- (*********************************** TLWLayr **********************************)
- destructor TLWLayr.Destroy;
- begin
- inherited;
- end;
- class function TLWLayr.GetID: TID4;
- begin
- result := ID_LAYR;
- end;
- procedure TLWLayr.LoadData(AStream: TStream; DataStart, DataSize: LongWord);
- begin
- ReadMotorolaNumber(AStream, @FNumber, 2);
- ReadMotorolaNumber(AStream, @FFlags, 2);
- ReadMotorolaNumber(AStream, @FPivot, 4, 3);
- ReadS0(AStream, FName);
- if ((DataStart + DataSize) - Cardinal(AStream.Position)) > 2 then
- ReadMotorolaNumber(AStream, @FParent, 2);
- end;
- (*********************************** TLWSurf **********************************)
- destructor TLWSurf.Destroy;
- begin
- inherited;
- end;
- class function TLWSurf.GetID: TID4;
- begin
- result := ID_SURF;
- end;
- function TLWSurf.GetParamAddr(Param: TID4): Pointer;
- var
- Idx: Integer;
- sParam: string;
- begin
- result := inherited GetParamAddr(Param);
- if (result = nil) and (Source <> '') then
- begin
- sParam := string(Param);
- Idx := RootChunks.FindChunk(@FindSurfaceByName, @sParam, 0);
- if Idx <> -1 then
- result := TLWSurf(RootChunks[Idx]).ParamAddr[Param];
- end;
- end;
- function TLWSurf.GetSurfId: Integer;
- var
- c, SurfIdx: Integer;
- begin
- c := 0;
- SurfIdx := Owner.FindChunk(@FindChunkById, @ID_SURF);
- while (SurfIdx <> -1) and (Owner[SurfIdx] <> Self) do
- begin
- SurfIdx := Owner.FindChunk(@FindChunkById, @ID_SURF, SurfIdx + 1);
- Inc(c);
- end;
- result := c;
- end;
- procedure TLWSurf.LoadData(AStream: TStream; DataStart, DataSize: LongWord);
- var
- CurId: TID4;
- begin
- ReadS0(AStream, FName);
- ReadS0(AStream, FSource);
- while Cardinal(AStream.Position) < (DataStart + DataSize) do
- begin
- AStream.Read(CurId, 4);
- Items.Add(GetChunkClass(CurId, TLWSubChunk).Create);
- with Items[Items.Count - 1] do
- begin
- FID := CurId;
- LoadFromStream(AStream);
- end;
- end;
- end;
- (*********************************** TLWPTag **********************************)
- constructor TLWPTag.Create;
- begin
- inherited;
- end;
- function TLWPTag.AddTag(Value: TU2): Integer;
- var
- i, L: Integer;
- begin
- result := -1;
- L := Length(FTags);
- for i := 0 to L - 1 do
- if Value = FTags[i] then
- begin
- result := i;
- Exit;
- end;
- if result = -1 then
- begin
- SetLength(FTags, L + 1);
- FTags[L] := Value;
- result := L;
- end;
- end;
- procedure TLWPTag.Clear;
- begin
- SetLength(FTagMaps, 0);
- SetLength(FTags, 0);
- end;
- function TLWPTag.GetPolsByTag(tag: TU2; var PolyIndices: TU2DynArray): Integer;
- var
- i: Integer;
- procedure AddPoly(Value: TU2);
- var
- L: Integer;
- begin
- L := Length(PolyIndices);
- SetLength(PolyIndices, L + 1);
- PolyIndices[L] := Value;
- end;
- begin
- for i := 0 to TagMapCount - 1 do
- if TagMaps[i].tag = tag then
- AddPoly(TagMaps[i].poly);
- result := Length(PolyIndices);
- end;
- class function TLWPTag.GetID: TID4;
- begin
- result := ID_PTAG;
- end;
- function TLWPTag.GetTag(AIndex: Integer): TU2;
- begin
- ValidateTagInfo;
- result := FTags[AIndex];
- end;
- function TLWPTag.GetTagCount: Integer;
- begin
- ValidateTagInfo;
- result := Length(FTags);
- end;
- function TLWPTag.GetTagMapCount: Integer;
- begin
- result := Length(FTagMaps) div 2;
- end;
- function TLWPTag.GetTagMaps(AIndex: Integer): TLWPolyTagMap;
- begin
- result := PLWPolyTagMap(@FTagMaps[AIndex * 2])^;
- end;
- procedure TLWPTag.LoadData(AStream: TStream; DataStart, DataSize: LongWord);
- var
- Idx: Integer;
- begin
- Idx := 0;
- with AStream do
- begin
- Read(FMapType, 4);
- SetLength(FTagMaps, (DataSize - 4) div 2);
- while Cardinal(Position) < (DataStart + DataSize) do
- begin
- ReadVXAsU2(AStream, @FTagMaps[Idx]);
- ReadMotorolaNumber(AStream, @FTagMaps[Idx + 1], 2);
- Inc(Idx, 2);
- end;
- // correct length guestimate errors if any
- if (Idx + 1) < Length(FTagMaps) then
- SetLength(FTagMaps, Idx + 1);
- end;
- end;
- procedure TLWPTag.ValidateTagInfo;
- var
- i: Integer;
- begin
- if Length(FTags) > 0 then
- Exit;
- for i := 0 to TagMapCount - 1 do
- AddTag(TagMaps[i].tag);
- end;
- (******************************** TLWParentChunk ******************************)
- procedure TLWParentChunk.Clear;
- begin
- FreeAndNil(FItems);
- inherited;
- end;
- function TLWParentChunk.GetFloatParam(Param: TID4): Single;
- var
- pdata: Pointer;
- begin
- pdata := ParamAddr[Param];
- if pdata <> nil then
- begin
- result := PF4(pdata)^;
- ReverseByteOrder(@result, 4);
- end
- else
- result := 0.0;
- end;
- function TLWParentChunk.GetItems: TLWChunkList;
- begin
- if FItems = nil then
- FItems := TLWChunkList.Create(true, Self);
- result := FItems;
- end;
- function TLWParentChunk.GetLongParam(Param: TID4): LongWord;
- var
- pdata: Pointer;
- begin
- pdata := ParamAddr[Param];
- if pdata <> nil then
- begin
- result := PU4(pdata)^;
- ReverseByteOrder(@result, 4);
- end
- else
- result := 0;
- end;
- function TLWParentChunk.GetParamAddr(Param: TID4): Pointer;
- var
- Idx: Integer;
- begin
- result := nil;
- Idx := Items.FindChunk(@FindChunkById, @Param, 0);
- if Idx <> -1 then
- result := Items[Idx].data;
- end;
- function TLWPols.GetPolsByPntIdx(VertIdx: TU2;
- var VertPolys: TU2DynArray): Integer;
- var
- i, j, L: Integer;
- begin
- L := 0;
- if Length(VertPolys) > 0 then
- SetLength(VertPolys, 0);
- for i := 0 to PolsCount - 1 do
- begin
- for j := 1 to Indices[PolsByIndex[i]] do
- begin
- if Indices[PolsByIndex[i] + j] = VertIdx then
- begin
- L := Length(VertPolys);
- SetLength(VertPolys, L + 1);
- VertPolys[L] := i;
- end;
- end;
- end;
- result := L;
- end;
- function TLWChunkList.Add(AChunk: TLWChunk): Integer;
- begin
- if (FOwner <> nil) and (FOwner is TLWParentChunk) then
- AChunk.FParentChunk := TLWParentChunk(FOwner);
- AChunk.FOwner := Self;
- result := inherited Add(AChunk);
- end;
- procedure TLWPols.CalcPntsNormals;
- var
- i, j, k, PntIdx, PolyIdx, SurfIdx: Integer;
- Pnts: TLWPnts;
- // PTags: TLWPTag;
- TmpAddr: Pointer;
- sman: TF4;
- begin
- // Todo: CalcPntsNormals
- if IndiceCount = 0 then
- Exit;
- with ParentChunk as TLWLayr do
- Pnts := TLWPnts(Items[Items.FindChunk(@FindChunkById, @ID_PNTS, 0)]);
- for PolyIdx := 0 to PolsCount - 1 do
- begin
- i := PolsByIndex[PolyIdx];
- SurfIdx := RootChunks.FindChunk(@FindSurfaceByTag,
- @FPolsInfo[PolyIdx].surfid);
- TmpAddr := TLWSurf(RootChunks[SurfIdx]).ParamAddr[ID_SMAN];
- if TmpAddr <> nil then
- begin
- sman := PF4(TmpAddr)^;
- ReverseByteOrder(@sman, 4);
- end
- else
- sman := 0;
- for j := 1 to Indices[i] do
- begin
- FPolsInfo[PolyIdx].vnorms[j - 1] := FPolsInfo[PolyIdx].norm;
- if sman <= 0 then
- continue;
- PntIdx := Indices[i + j];
- for k := 0 to Pnts.PntsInfo[PntIdx].npols - 1 do
- begin
- if Pnts.PntsInfo[PntIdx].pols[k] = PolyIdx then
- continue;
- if ArcCos(VecDot(FPolsInfo[PolyIdx].norm,
- FPolsInfo[Pnts.PntsInfo[PntIdx].pols[k]].norm)) > sman then
- continue;
- FPolsInfo[PolyIdx].vnorms[j - 1] :=
- VecAdd(FPolsInfo[PolyIdx].vnorms[j - 1],
- FPolsInfo[Pnts.PntsInfo[PntIdx].pols[k]].norm);
- end;
- FPolsInfo[PolyIdx].vnorms[j - 1] :=
- VecNorm(FPolsInfo[PolyIdx].vnorms[j - 1]);
- end;
- end;
- end;
- function TLWChunk.GetRootChunks: TLWChunkList;
- var
- Parent: TLWParentChunk;
- begin
- result := nil;
- if (FParentChunk = nil) then
- begin
- if (FOwner is TLWChunkList) then
- begin
- result := FOwner;
- Exit;
- end;
- end
- else
- begin
- Parent := FParentChunk;
- while not(Parent.ParentChunk = nil) do
- Parent := Parent.ParentChunk;
- result := Parent.Owner;
- end;
- end;
- function TLWChunkList.FindChunk(ChunkFind: TLWChunkFind; Criteria: Pointer;
- StartIndex: Integer): Integer;
- var
- Found: boolean;
- begin
- Found := false;
- result := -1;
- while (StartIndex < Count) and (not Found) do
- begin
- ChunkFind(Items[StartIndex], Criteria, Found);
- if Found then
- begin
- result := StartIndex;
- Exit;
- end;
- Inc(StartIndex);
- end;
- end;
- function TLWChunk.GetIndex: Integer;
- begin
- result := Owner.IndexOf(Self);
- end;
- procedure TLWChunk.Loaded;
- begin
- // do nothing
- end;
- procedure TLWChunkList.Loaded;
- var
- i: Integer;
- begin
- for i := 0 to Count - 1 do
- begin
- Items[i].Loaded;
- end;
- end;
- function TLWParentChunk.GetVec3Param(Param: TID4): TVec12;
- var
- pdata: Pointer;
- begin
- pdata := ParamAddr[Param];
- if pdata <> nil then
- begin
- result := PVec12(pdata)^;
- ReverseByteOrder(@result, 4, 3);
- end
- else
- begin
- result[0] := 0;
- result[1] := 1;
- result[2] := 2;
- end;
- end;
- function TLWParentChunk.GetVXParam(Param: TID4): Word;
- var
- pdata: Pointer;
- begin
- pdata := ParamAddr[Param];
- if pdata <> nil then
- result := ValueOfVX(pdata)
- else
- result := 0;
- end;
- function TLWParentChunk.GetWordParam(Param: TID4): Word;
- var
- pdata: Pointer;
- begin
- pdata := ParamAddr[Param];
- if pdata <> nil then
- begin
- result := PU4(pdata)^;
- ReverseByteOrder(@result, 2);
- end
- else
- result := 0;
- end;
- procedure TLWParentChunk.Loaded;
- begin
- Items.Loaded;
- end;
- procedure TLWPols.Loaded;
- begin
- inherited;
- CalcPntsNormals;
- end;
- function TLWObjectFile.TagToName(tag: TU2): string;
- var
- TagsIdx: Integer;
- begin
- TagsIdx := Chunks.FindChunk(@FindChunkById, @ID_TAGS);
- if TagsIdx <> -1 then
- result := TLWTags(Chunks[TagsIdx]).TagToName(tag);
- end;
- (******************** TLWClip ********************)
- class function TLWClip.GetID: TID4;
- begin
- result := ID_CLIP;
- end;
- procedure TLWClip.LoadData(AStream: TStream; DataStart, DataSize: LongWord);
- var
- CurId: TID4;
- begin
- ReadMotorolaNumber(AStream, @FClipIndex, 4);
- while Cardinal(AStream.Position) < (DataStart + DataSize) do
- begin
- AStream.Read(CurId, 4);
- Items.Add(GetChunkClass(CurId, TLWSubChunk).Create);
- with Items[Items.Count - 1] do
- begin
- FID := CurId;
- LoadFromStream(AStream);
- end;
- end;
- end;
- // TLWContentDir
- (* function TLWContentDir.ContentSearch(AFilename: string): string;
- var
- i: Integer;
- begin
- if not FileExists(AFilename) then
- begin
- result := ExtractFileName(AFilename);
- if not FileExists(result) then
- begin
- for i := 0 to SubDirs.Count - 1 do
- begin
- if FileExists(Root+'\'+SubDirs[i]+'\'+result) then
- begin
- result:=Root+'\'+SubDirs[i]+'\'+result;
- Exit;
- end;
- end;
- result := '';
- end;
- end;
- end;
- *)
- destructor TLWContentDir.Destroy;
- begin
- FreeAndNil(FSubDirs);
- inherited;
- end;
- function TLWContentDir.FindContent(AFilename: string): string;
- var
- i: Integer;
- begin
- if not FileExists(AFilename) then
- begin
- result := ExtractFileName(AFilename);
- if not FileExists(result) then
- begin
- for i := 0 to SubDirs.Count - 1 do
- begin
- if FileExists(Root + '\' + SubDirs[i] + '\' + result) then
- begin
- result := Root + '\' + SubDirs[i] + '\' + result;
- Exit;
- end;
- end;
- result := '';
- end;
- end;
- end;
- function TLWContentDir.GetSubDirs: TStrings;
- begin
- if FSubDirs = nil then
- FSubDirs := TStringList.Create;
- result := FSubDirs;
- end;
- procedure TLWContentDir.SetRoot(const Value: string);
- begin
- FRoot := Value;
- end;
- procedure TLWContentDir.SetSubDirs(const Value: TStrings);
- begin
- SubDirs.Assign(Value);
- end;
- initialization
- // Pnts
- RegisterChunkClass(TLWPnts);
- // Pols
- RegisterChunkClass(TLWPols);
- // VMap
- RegisterChunkClass(TLWVMap);
- // Tags
- RegisterChunkClass(TLWTags);
- // PTAG
- RegisterChunkClass(TLWPTag);
- // SURF
- RegisterChunkClass(TLWSurf);
- // LAYR
- RegisterChunkClass(TLWLayr);
- // CLIP
- RegisterChunkClass(TLWClip);
- finalization
- // UnRegisterChunkClasses;
- FreeAndNil(ChunkClasses);
- FreeAndNil(ContentDir);
- end.
|