PDA

View Full Version : Export LWO in a readable text file



GPM
03-01-2007, 02:14 PM
Hello all,
Here's the problem:
I need to get vertex information for a character object including endomorph vertex information. I need this information in a format that can be read in a text format, such as .xml, or anything that can be opened up easily for parsing.

Any help is appreciated.

Lone Angel
03-02-2007, 12:16 PM
LWO itself is quite easily parsed. There's sample in SDK that prints out contents of LWO-file.

GPM
03-02-2007, 01:18 PM
Thanks Lone Angel!
I'll check the SDK out to generate the files I need.

regularfry
03-11-2007, 03:17 PM
...and don't mind a bit of hackery, I wrote a Ruby library to parse LWO files a little while ago. It's here: http://rubyforge.org/projects/lightwave/

GPM
03-13-2007, 09:09 AM
Thanks, I'm a little bogged down with other things, but I will check it out the ruby parser. I am just trying to get vertex information for the instances where morph animations are played in addition to the bone animation.

Adrian Lopez
03-14-2007, 01:08 AM
You should check out the spec for Lightwave object files (http://www.newtek.com/lightwave/developer/LW80/8lwsdk/docs/filefmts/lwo2.html). Morphs are kept as vertex maps, so in order to load them you'll want to look for VMAP chunks of type MORF. The base positions will be found inside PNTS chunks, and polygon data inside POLS chunks.

Here are some C routines for parsing LW object files. I don't guarantee these will work, but they might. I'm releasing them as public domain:

loadlwio.h:

#ifndef _LOADLWIO_H
#define _LOADLWIO_H

#include <stdio.h>

struct ChunkDescriptor {
FILE *file; // pointer to file containing chunk.
long fptr; // offset of chunk within file.
unsigned long length; // length of chunk.
unsigned long cptr; // offset into current chunk from beginning of chunk.
unsigned long id; // chunk id.
struct ChunkDescriptor *parent; // indicate parent of subchunk
};

int LoadUBYTE(unsigned char *b, ChunkDescriptor *chunk);
int LoadBYTE(char *b, ChunkDescriptor *chunk);
int LoadBigEndianUWORD(unsigned short *vout, ChunkDescriptor *chunk);
int LoadBigEndianWORD(short *vout, ChunkDescriptor *chunk);
int LoadBigEndianULONG(unsigned long *vout, ChunkDescriptor *chunk);
int LoadBigEndianLONG(long *vout, ChunkDescriptor *chunk);
int LoadBigEndianFLOAT4(float *vout, ChunkDescriptor *chunk);
int LoadID4(unsigned long *l, ChunkDescriptor *chunk);
int LoadVX(unsigned long *l, ChunkDescriptor *chunk);
char *LoadS0(ChunkDescriptor *chunk);

int OpenIFF(struct ChunkDescriptor *form, FILE *f);
int LoadChunk(struct ChunkDescriptor *chunk, ChunkDescriptor *parent_chunk);
int LoadSubChunk(struct ChunkDescriptor *chunk, ChunkDescriptor *parent_chunk);

int AdvancePastEndOfChunk(ChunkDescriptor *chunk);
int EndOfChunk(ChunkDescriptor *chunk);

#endif

loadlwio.cpp:

#include <string.h>
#include <malloc.h>
#include "loadlwio.h"
#include "loadlwids.h"

/* Increment the chunk's internal offset value (cptr) by specified increment, and
do the same for the chunk's parents. This should be done in order to keep the
offset in sync with the file containing the chunk.
*/
void IncrementChunkOffset(ChunkDescriptor *chunk, unsigned long increment)
{
ChunkDescriptor *t;

t = chunk;

while (t)
{
t->cptr += increment;
t = t->parent;
}
}


/* Determine whether the chunk's pointer has reached the end of the chunk.
*/
int EndOfChunk(ChunkDescriptor *chunk)
{
if (chunk->cptr >= chunk->length) return 1;

return 0;
}


/* Load a 16-bit unsigned big-endian integer from the specified chunk.
*/
int LoadBigEndianUWORD(unsigned short *vout, ChunkDescriptor *chunk)
{
unsigned char *c = (unsigned char*) vout;

for (int x = sizeof(unsigned short) - 1; x >= 0; x--) {
if (EndOfChunk(chunk)) return 0;
/* WAS: if ((chunk->cptr + x) >= chunk->length) return 0; */
if (fread(c + x, sizeof(unsigned char), 1, chunk->file) != 1)
return 0;
IncrementChunkOffset(chunk, 1);
}

return 1;
}


/* Load a 16-bit signed big-endian integer from the specified chunk.
*/
int LoadBigEndianWORD(short *vout, ChunkDescriptor *chunk)
{
return LoadBigEndianUWORD((unsigned short*)vout, chunk);
}


/* Load a 32-bit unsigned big-endian integer from the specified chunk.
*/
int LoadBigEndianULONG(unsigned long *vout, ChunkDescriptor *chunk)
{
unsigned char *c = (unsigned char*) vout;

for (int x = sizeof(unsigned long) - 1; x >= 0; x--) {
if (EndOfChunk(chunk)) return 0;
/* WAS: if ((chunk->cptr + x) >= chunk->length) return 0; */
if (fread(c + x, sizeof(unsigned char), 1, chunk->file) != 1)
return 0;
IncrementChunkOffset(chunk, 1);
}

return 1;
}


/* Load a 32-bit signed big-endian integer from the specified chunk.
*/
int LoadBigEndianLONG(long *vout, ChunkDescriptor *chunk)
{
return LoadBigEndianULONG((unsigned long*)vout, chunk);
}


/* Load a 32-bit signed big-endian floating point value from the specified chunk.
*/
int LoadBigEndianFLOAT4(float *vout, ChunkDescriptor *chunk)
{
return LoadBigEndianULONG((unsigned long*)vout, chunk);
}


/* Load an 8-bit unsigned integer from the specified chunk.
*/
int LoadUBYTE(unsigned char *b, ChunkDescriptor *chunk)
{
if (EndOfChunk(chunk)) return 0;
if (fread(b, sizeof(unsigned char), 1, chunk->file) != 1)
return 0;
IncrementChunkOffset(chunk, 1);

return 1;
}


/* Load an 8-bit signed integer from the specified chunk.
*/
int LoadBYTE(char *b, ChunkDescriptor *chunk)
{
return LoadUBYTE((unsigned char*) b, chunk);
}

int LoadID4(unsigned long *l, ChunkDescriptor *chunk)
{
return LoadBigEndianULONG(l, chunk);
}

int LoadVX(unsigned long *l, ChunkDescriptor *chunk)
{
unsigned short vx1, vx2;

if (!LoadBigEndianUWORD(&vx1, chunk)) return 0;


if (vx1 < 0xFF00) {
*l = vx1;
} else {
if (!LoadBigEndianUWORD(&vx2, chunk)) return 0;
*l = (((unsigned long)(vx1 & 0x00FF)) << 16) | (unsigned long)vx2;
}

return 1;
}

void AdvancePastEndOfString(ChunkDescriptor *chunk)
{
char t;

/* Advance file pointer past end of string and return. */

do {
LoadBYTE(&t, chunk);
} while(t != '\0');


/* Next item always begins at even offset. */

if (ftell(chunk->file) % 2 == 1)
LoadBYTE(&t, chunk);
}

char *LoadS0(ChunkDescriptor *chunk)
{
const int alloc_size = 8192;
int c = 0;

char *stemp = (char*) malloc(alloc_size);
if (!stemp) {
AdvancePastEndOfString(chunk);
return 0;
}

do {
if (c > (alloc_size * (c / alloc_size + 1))) {
char *newmem = (char*) realloc(stemp, alloc_size * (c / alloc_size + 1));
if (!newmem) {
AdvancePastEndOfString(chunk);
free(stemp);
return 0;
}

stemp = newmem;
}

LoadBYTE(stemp + c, chunk);
} while (stemp[c++] != '\0');

char t;
if (ftell(chunk->file) % 2 == 1)
LoadBYTE(&t, chunk);

char *sreturn = (char*) malloc(c);
if (!sreturn) {
free(stemp);
return 0;
}

strcpy(sreturn, stemp);
free(stemp);

return sreturn;
}

/* Open a file-level IFF pseudo-chunk. A pseudo-chunk is an implicit chunk with no ID
that begins and ends at file boundaries.
*/
int OpenIFF(struct ChunkDescriptor *form, FILE *f)
{
long file_length;

if (fseek(f, 0, SEEK_END) != 0) return 0;

file_length = ftell(f);
if (file_length == -1L) return 0;

if (fseek(f, 0, SEEK_SET) != 0) return 0;

form->file = f;
form->fptr = 0;
form->cptr = 0;
form->id = CID_FILE_BEGIN;
form->length = file_length;
form->parent = 0;

return 1;
}

/* Open an IFF chunk.
*/
int LoadChunk(struct ChunkDescriptor *chunk, ChunkDescriptor *parent_chunk)
{
if (!LoadID4(&chunk->id, parent_chunk) || !LoadBigEndianULONG(&chunk->length, parent_chunk))
return 0;

chunk->parent = parent_chunk;
chunk->cptr = 0;
chunk->file = parent_chunk->file;
chunk->fptr = ftell(chunk->file);
if (chunk->fptr == -1L) return 0;

return 1;
}

/* Open a LWO2 subchunk.
*/
int LoadSubChunk(struct ChunkDescriptor *chunk, ChunkDescriptor *parent_chunk)
{
unsigned short length;

/* Chunk always begins at even offset. */

if (ftell(parent_chunk->file) % 2 == 1) {
unsigned char t;
LoadUBYTE(&t, parent_chunk);
}

if (!LoadID4(&chunk->id, parent_chunk) || !LoadBigEndianUWORD(&length, parent_chunk))
return 0;

chunk->length = length;
chunk->cptr = 0;
chunk->file = parent_chunk->file;
chunk->fptr = ftell(chunk->file);
if (chunk->fptr == -1L) return 0;
chunk->parent = parent_chunk;

return 1;
}

int AdvancePastEndOfChunk(ChunkDescriptor *chunk)
{
if (fseek(chunk->file, chunk->fptr + chunk->length, SEEK_SET) != 0) return 0; // seek next chunk.

chunk->fptr = ftell(chunk->file);
if (chunk->fptr == -1L) return 0;

IncrementChunkOffset(chunk, chunk->length - chunk->cptr);

if ((chunk->fptr % 2) == 1) {
if (fseek(chunk->file, SEEK_CUR, 1) != 0) // start reading from even offset.
return 0;
IncrementChunkOffset(chunk, 1);
}

return 1;
}




loadlwids.h:

#ifndef _LOADLWIDS_H
#define _LOADLWIDS_H

#define C4TOLONG(a,b,c,d) ( (unsigned long)a << 24 | (unsigned long)b << 16 | (unsigned long)c << 8 | (unsigned long)d )

#define CID_FILE_BEGIN 0
#define CID_FORM C4TOLONG('F','O','R','M')
#define CID_LAYR C4TOLONG('L','A','Y','R')
#define CID_PNTS C4TOLONG('P','N','T','S')
#define CID_VMAP C4TOLONG('V','M','A','P')
#define CID_POLS C4TOLONG('P','O','L','S')
#define CID_TAGS C4TOLONG('T','A','G','S')
#define CID_PTAG C4TOLONG('P','T','A','G')
#define CID_VMAD C4TOLONG('V','M','A','D')
#define CID_ENVL C4TOLONG('E','N','V','L')
#define CID_CLIP C4TOLONG('C','L','I','P')
#define CID_SURF C4TOLONG('S','U','R','F')
#define CID_BBOX C4TOLONG('B','B','O','X')
#define CID_DESC C4TOLONG('D','E','S','C')
#define CID_TEXT C4TOLONG('T','E','X','T')
#define CID_ICON C4TOLONG('I','C','O','N')

#define CID_TYPE C4TOLONG('T','Y','P','E')
#define CID_PRE C4TOLONG('P','R','E',' ')
#define CID_POST C4TOLONG('P','O','S','T')
#define CID_KEY C4TOLONG('K','E','Y',' ')
#define CID_SPAN C4TOLONG('S','P','A','N')
#define CID_CHAN C4TOLONG('C','H','A','N')
#define CID_NAME C4TOLONG('N','A','M','E')
#define CID_COLR C4TOLONG('C','O','L','R')
#define CID_DIFF C4TOLONG('D','I','F','F')
#define CID_LUMI C4TOLONG('L','U','M','I')
#define CID_SPEC C4TOLONG('S','P','E','C')
#define CID_REFL C4TOLONG('R','E','F','L')
#define CID_TRAN C4TOLONG('T','R','A','N')
#define CID_TRNL C4TOLONG('T','R','N','L')
#define CID_GLOS C4TOLONG('G','L','O','S')
#define CID_SHRP C4TOLONG('S','H','R','P')
#define CID_BUMP C4TOLONG('B','U','M','P')
#define CID_SMAN C4TOLONG('S','M','A','N')
#define CID_RFOP C4TOLONG('R','F','O','P')
#define CID_RIMG C4TOLONG('R','I','M','G')
#define CID_RSAN C4TOLONG('R','S','A','N')
#define CID_RBLR C4TOLONG('R','B','L','R')
#define CID_RIND C4TOLONG('R','I','N','D')
#define CID_TROP C4TOLONG('T','R','O','P')
#define CID_TIMG C4TOLONG('T','I','M','G')
#define CID_TBLR C4TOLONG('T','B','L','R')
#define CID_CLRH C4TOLONG('C','L','R','H')
#define CID_CLRF C4TOLONG('C','L','R','F')
#define CID_ADTR C4TOLONG('A','D','T','R')
#define CID_GLOW C4TOLONG('G','L','O','W')
#define CID_GVAL C4TOLONG('G','V','A','L')
#define CID_LINE C4TOLONG('L','I','N','E')
#define CID_VCOL C4TOLONG('V','C','O','L')
#define CID_BLOK C4TOLONG('B','L','O','K')
#define CID_ENAB C4TOLONG('E','N','A','B')
#define CID_OPAC C4TOLONG('O','P','A','C')
#define CID_AXIS C4TOLONG('A','X','I','S')
#define CID_CNTR C4TOLONG('C','N','T','R')
#define CID_SIZE C4TOLONG('S','I','Z','E')
#define CID_ROTA C4TOLONG('R','O','T','A')
#define CID_OREF C4TOLONG('O','R','E','F')
#define CID_FALL C4TOLONG('F','A','L','L')
#define CID_CSYS C4TOLONG('C','S','Y','S')
#define CID_PROJ C4TOLONG('P','R','O','J')
#define CID_IMAP C4TOLONG('I','M','A','P')
#define CID_WRAP C4TOLONG('W','R','A','P')
#define CID_WRPW C4TOLONG('W','R','P','W')
#define CID_WRPH C4TOLONG('W','R','P','H')
#define CID_AAST C4TOLONG('A','A','S','T')
#define CID_PIXB C4TOLONG('P','I','X','B')
#define CID_STCK C4TOLONG('S','T','C','K')
#define CID_TAMP C4TOLONG('T','A','M','P')
#define CID_VALU C4TOLONG('V','A','L','U')
#define CID_FUNC C4TOLONG('F','U','N','C')
#define CID_IMAG C4TOLONG('I','M','A','G')
#define CID_IMAP C4TOLONG('I','M','A','P')
#define CID_PROC C4TOLONG('P','R','O','C')
#define CID_GRAD C4TOLONG('G','R','A','D')
#define CID_SHDR C4TOLONG('S','H','D','R')
#define CID_TMAP C4TOLONG('T','M','A','P')
#define CID_PNAM C4TOLONG('P','N','A','M')
#define CID_INAM C4TOLONG('I','N','A','M')
#define CID_GRST C4TOLONG('G','R','S','T')
#define CID_GREN C4TOLONG('G','R','E','N')
#define CID_GRPT C4TOLONG('G','R','P','T')
#define CID_FKEY C4TOLONG('F','K','E','Y')
#define CID_IKEY C4TOLONG('I','K','E','Y')

#define FORM_LWO2 C4TOLONG('L','W','O','2')

#define VMAP_TYPE_PICK C4TOLONG('P','I','C','K');
#define VMAP_TYPE_WGHT C4TOLONG('W','G','H','T');
#define VMAP_TYPE_MNVW C4TOLONG('M','N','V','W');
#define VMAP_TYPE_TXUV C4TOLONG('T','X','U','V');
#define VMAP_TYPE_RGB C4TOLONG('R','G','B',' ');
#define VMAP_TYPE_RGBA C4TOLONG('R','G','B','A');
#define VMAP_TYPE_MORF C4TOLONG('M','O','R','F');
#define VMAP_TYPE_SPOT C4TOLONG('S','P','O','T');

#endif

GPM
03-14-2007, 08:25 AM
Thanks Adrian.
I will check out the parser. I plan on reporting back to forum when complete. It will be a couple of days before I get to it.

I appreciate eveyone's help.

GPM
03-14-2007, 02:41 PM
As long as I am spending the time to get the endomorph changes,

is there a plugin that will export the animation on the timeline into a sequenced vertex list?

Right now I am planning on using a combination of bone animation and morphs, but the bone weights in the Game Engine don't appear the same way they do in LightWave.

If I had all of my animations were exported as vertex animations, I wouldn't have to worry about how the bones deform or how and when to apply morphs. I would end up with WYSIWYG from LightWave to the engine.

zardoz
03-15-2007, 04:56 AM
from what I've read on these forums, I guess that's what Point Oven does. Maybe you should check that. I ask other users to correct me if I'm wrong, because I don't have Point Oven.

GPM
03-17-2007, 10:00 AM
Thank you. Point Oven is a good solution to this problem. I thank everyone for their help on this.