1 /* -----------------------------------------------------------------------------
2 
3 PicoModel Library
4 
5 Copyright (c) 2002, Randy Reddig & seaw0lf
6 All rights reserved.
7 
8 Redistribution and use in source and binary forms, with or without modification,
9 are permitted provided that the following conditions are met:
10 
11 Redistributions of source code must retain the above copyright notice, this list
12 of conditions and the following disclaimer.
13 
14 Redistributions in binary form must reproduce the above copyright notice, this
15 list of conditions and the following disclaimer in the documentation and/or
16 other materials provided with the distribution.
17 
18 Neither the names of the copyright holders nor the names of its contributors may
19 be used to endorse or promote products derived from this software without
20 specific prior written permission.
21 
22 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
23 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
26 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
27 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
29 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 
33 ----------------------------------------------------------------------------- */
34 
35 /* marker */
36 #define PM_LWO_C
37 
38 /* dependencies */
39 #include "picointernal.h"
40 #include "lwo/lwo2.h"
41 
42 /* uncomment when debugging this module */
43 /*#define DEBUG_PM_LWO*/
44 
45 #ifdef DEBUG_PM_LWO
46 #include "time.h"
47 #endif
48 
49 /* helper functions */
lwo_lwIDToStr(unsigned int lwID)50 static const char *lwo_lwIDToStr( unsigned int lwID )
51 {
52 	static char lwIDStr[5];
53 
54 	if (!lwID)
55 	{
56 		return "n/a";
57 	}
58 
59 	lwIDStr[ 0 ] = (char)((lwID) >> 24);
60 	lwIDStr[ 1 ] = (char)((lwID) >> 16);
61 	lwIDStr[ 2 ] = (char)((lwID) >> 8);
62 	lwIDStr[ 3 ] = (char)((lwID));
63 	lwIDStr[ 4 ] = '\0';
64 
65 	return lwIDStr;
66 }
67 
68 /*
69 _lwo_canload()
70 validates a LightWave Object model file. btw, i use the
71 preceding underscore cause it's a static func referenced
72 by one structure only.
73 */
_lwo_canload(PM_PARAMS_CANLOAD)74 static int _lwo_canload( PM_PARAMS_CANLOAD )
75 {
76 	picoMemStream_t *s;
77 	unsigned int failID = 0;
78 	int failpos = -1;
79 	int ret;
80 
81 	/* create a new pico memorystream */
82 	s = _pico_new_memstream( (picoByte_t *)buffer, bufSize );
83 	if (s == NULL)
84 	{
85 		return PICO_PMV_ERROR_MEMORY;
86 	}
87 
88 	ret = lwValidateObject( fileName, s, &failID, &failpos );
89 
90 	_pico_free_memstream( s );
91 
92 	return ret;
93 }
94 
95 /*
96 _lwo_load()
97 loads a LightWave Object model file.
98 */
_lwo_load(PM_PARAMS_LOAD)99 static picoModel_t *_lwo_load( PM_PARAMS_LOAD )
100 {
101 	picoMemStream_t	*s;
102 	unsigned int	failID = 0;
103 	int				failpos = -1;
104 	lwObject		*obj;
105 	lwSurface		*surface;
106 	lwLayer			*layer;
107 	lwPoint			*pt;
108 	lwPolygon		*pol;
109 	lwPolVert		*v;
110 	lwVMapPt		*vm;
111 	char			name[ 256 ];
112 	int				i, j, k, numverts;
113 
114 	picoModel_t		*picoModel;
115 	picoSurface_t	*picoSurface;
116 	picoShader_t	*picoShader;
117 	picoVec3_t		xyz, normal;
118 	picoVec2_t		st;
119 	picoColor_t		color;
120 
121 	int				defaultSTAxis[ 2 ];
122 	picoVec2_t		defaultXYZtoSTScale;
123 
124 	picoVertexCombinationHash_t **hashTable;
125 	picoVertexCombinationHash_t	*vertexCombinationHash;
126 
127 #ifdef DEBUG_PM_LWO
128 	clock_t load_start, load_finish, convert_start, convert_finish;
129 	double load_elapsed, convert_elapsed;
130 
131 	load_start = clock();
132 #endif
133 
134 	/* do frame check */
135 	if( frameNum < 0 || frameNum >= 1 )
136 	{
137 		_pico_printf( PICO_ERROR, "Invalid or out-of-range LWO frame specified" );
138 		return NULL;
139 	}
140 
141 	/* create a new pico memorystream */
142 	s = _pico_new_memstream( (picoByte_t *)buffer, bufSize );
143 	if (s == NULL)
144 	{
145 		return NULL;
146 	}
147 
148 	obj = lwGetObject( fileName, s, &failID, &failpos );
149 
150 	_pico_free_memstream( s );
151 
152 	if( !obj ) {
153 		_pico_printf( PICO_ERROR, "Couldn't load LWO file, failed on ID '%s', position %d", lwo_lwIDToStr( failID ), failpos );
154 		return NULL;
155 	}
156 
157 #ifdef DEBUG_PM_LWO
158 	convert_start = load_finish = clock();
159 	load_elapsed = (double)(load_finish - load_start) / CLOCKS_PER_SEC;
160 #endif
161 
162 	/* -------------------------------------------------
163 	pico model creation
164 	------------------------------------------------- */
165 
166 	/* create a new pico model */
167 	picoModel = PicoNewModel();
168 	if (picoModel == NULL)
169 	{
170 		_pico_printf( PICO_ERROR, "Unable to allocate a new model" );
171 		return NULL;
172 	}
173 
174 	/* do model setup */
175 	PicoSetModelFrameNum( picoModel, frameNum );
176 	PicoSetModelNumFrames( picoModel, 1 );
177 	PicoSetModelName( picoModel, fileName );
178 	PicoSetModelFileName( picoModel, fileName );
179 
180 	/* create all polygons from layer[ 0 ] that belong to this surface */
181 	layer = &obj->layer[0];
182 
183 	/* warn the user that other layers are discarded */
184 	if (obj->nlayers > 1)
185 	{
186 		_pico_printf( PICO_WARNING, "LWO loader discards any geometry data not in Layer 1 (%d layers found)", obj->nlayers );
187 	}
188 
189 	/* initialize dummy normal */
190 	normal[ 0 ] = normal[ 1 ] = normal[ 2 ] = 0.f;
191 
192 	/* setup default st map */
193 	st[ 0 ] = st[ 1 ] = 0.f;	/* st[0] holds max, st[1] holds max par one */
194 	defaultSTAxis[ 0 ] = 0;
195 	defaultSTAxis[ 1 ] = 1;
196 	for( i = 0; i < 3; i++ )
197 	{
198 		float min = layer->bbox[ i ];
199 		float max = layer->bbox[ i + 3 ];
200 		float size = max - min;
201 
202 		if (size > st[ 0 ])
203 		{
204 			defaultSTAxis[ 1 ] = defaultSTAxis[ 0 ];
205 			defaultSTAxis[ 0 ] = i;
206 
207 			st[ 1 ] = st[ 0 ];
208 			st[ 0 ] = size;
209 		}
210 		else if (size > st[ 1 ])
211 		{
212 			defaultSTAxis[ 1 ] = i;
213 			st[ 1 ] = size;
214 		}
215 	}
216 	defaultXYZtoSTScale[ 0 ] = 4.f / st[ 0 ];
217 	defaultXYZtoSTScale[ 1 ] = 4.f / st[ 1 ];
218 
219 	/* LWO surfaces become pico surfaces */
220 	surface = obj->surf;
221 	while (surface)
222 	{
223 		/* allocate new pico surface */
224 		picoSurface = PicoNewSurface( picoModel );
225 		if (picoSurface == NULL)
226 		{
227 			_pico_printf( PICO_ERROR, "Unable to allocate a new model surface" );
228 			PicoFreeModel( picoModel );
229 			lwFreeObject( obj );
230 			return NULL;
231 		}
232 
233 		/* LWO model surfaces are all triangle meshes */
234 		PicoSetSurfaceType( picoSurface, PICO_TRIANGLES );
235 
236 		/* set surface name */
237 		PicoSetSurfaceName( picoSurface, surface->name );
238 
239 		/* create new pico shader */
240 		picoShader = PicoNewShader( picoModel );
241 		if (picoShader == NULL)
242 		{
243 			_pico_printf( PICO_ERROR, "Unable to allocate a new model shader" );
244 			PicoFreeModel( picoModel );
245 			lwFreeObject( obj );
246 			return NULL;
247 		}
248 
249 		/* detox and set shader name */
250 		strncpy( name, surface->name, sizeof(name) );
251 		_pico_first_token( name );
252 		_pico_setfext( name, "" );
253 		_pico_unixify( name );
254 		PicoSetShaderName( picoShader, name );
255 
256 		/* associate current surface with newly created shader */
257 		PicoSetSurfaceShader( picoSurface, picoShader );
258 
259 		/* copy indices and vertex data */
260 		numverts = 0;
261 
262 		hashTable = PicoNewVertexCombinationHashTable();
263 
264 		if (hashTable == NULL)
265 		{
266 			_pico_printf( PICO_ERROR, "Unable to allocate hash table" );
267 			PicoFreeModel( picoModel );
268 			lwFreeObject( obj );
269 			return NULL;
270 		}
271 
272 		for( i = 0, pol = layer->polygon.pol; i < layer->polygon.count; i++, pol++ )
273 		{
274 			/* does this polygon belong to this surface? */
275 			if (pol->surf != surface)
276 				continue;
277 
278 			/* we only support polygons of the FACE type */
279 			if (pol->type != ID_FACE)
280 			{
281 				_pico_printf( PICO_WARNING, "LWO loader discarded a polygon because it's type != FACE (%s)", lwo_lwIDToStr( pol->type ) );
282 				continue;
283 			}
284 
285 			/* NOTE: LWO has support for non-convex polygons, do we want to store them as well? */
286 			if (pol->nverts != 3)
287 			{
288 				_pico_printf( PICO_WARNING, "LWO loader discarded a polygon because it has != 3 verts (%d)", pol->nverts );
289 				continue;
290 			}
291 
292 			for( j = 0, v = pol->v; j < 3; j++, v++ )
293 			{
294 				pt = &layer->point.pt[ v->index ];
295 
296 				/* setup data */
297 				xyz[ 0 ] = pt->pos[ 0 ];
298 				xyz[ 1 ] = pt->pos[ 2 ];
299 				xyz[ 2 ] = pt->pos[ 1 ];
300 
301 /* doom3 lwo data doesn't seem to have smoothing-angle information */
302 #if 0
303         if(surface->smooth <= 0)
304         {
305           /* use face normals */
306 				  normal[ 0 ] = v->norm[ 0 ];
307 				  normal[ 1 ] = v->norm[ 2 ];
308 				  normal[ 2 ] = v->norm[ 1 ];
309         }
310         else
311 #endif
312         {
313           /* smooth normals later */
314 				  normal[ 0 ] = 0;
315 				  normal[ 1 ] = 0;
316 				  normal[ 2 ] = 0;
317         }
318 
319 				st[ 0 ] = xyz[ defaultSTAxis[ 0 ] ] * defaultXYZtoSTScale[ 0 ];
320 				st[ 1 ] = xyz[ defaultSTAxis[ 1 ] ] * defaultXYZtoSTScale[ 1 ];
321 
322 				color[ 0 ] = (picoByte_t)(surface->color.rgb[ 0 ] * surface->diffuse.val * 0xFF);
323 				color[ 1 ] = (picoByte_t)(surface->color.rgb[ 1 ] * surface->diffuse.val * 0xFF);
324 				color[ 2 ] = (picoByte_t)(surface->color.rgb[ 2 ] * surface->diffuse.val * 0xFF);
325 				color[ 3 ] = 0xFF;
326 
327 				/* set from points */
328 				for( k = 0, vm = pt->vm; k < pt->nvmaps; k++, vm++ )
329 				{
330 					if (vm->vmap->type == LWID_('T','X','U','V'))
331 					{
332 						/* set st coords */
333 						st[ 0 ] = vm->vmap->val[ vm->index ][ 0 ];
334 						st[ 1 ] = 1.f - vm->vmap->val[ vm->index ][ 1 ];
335 					}
336 					else if (vm->vmap->type == LWID_('R','G','B','A'))
337 					{
338 						/* set rgba */
339 						color[ 0 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 0 ] * surface->color.rgb[ 0 ] * surface->diffuse.val * 0xFF);
340 						color[ 1 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 1 ] * surface->color.rgb[ 1 ] * surface->diffuse.val * 0xFF);
341 						color[ 2 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 2 ] * surface->color.rgb[ 2 ] * surface->diffuse.val * 0xFF);
342 						color[ 3 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 3 ] * 0xFF);
343 					}
344 				}
345 
346 				/* override with polygon data */
347 				for( k = 0, vm = v->vm; k < v->nvmaps; k++, vm++ )
348 				{
349 					if (vm->vmap->type == LWID_('T','X','U','V'))
350 					{
351 						/* set st coords */
352 						st[ 0 ] = vm->vmap->val[ vm->index ][ 0 ];
353 						st[ 1 ] = 1.f - vm->vmap->val[ vm->index ][ 1 ];
354 					}
355 					else if (vm->vmap->type == LWID_('R','G','B','A'))
356 					{
357 						/* set rgba */
358 						color[ 0 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 0 ] * surface->color.rgb[ 0 ] * surface->diffuse.val * 0xFF);
359 						color[ 1 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 1 ] * surface->color.rgb[ 1 ] * surface->diffuse.val * 0xFF);
360 						color[ 2 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 2 ] * surface->color.rgb[ 2 ] * surface->diffuse.val * 0xFF);
361 						color[ 3 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 3 ] * 0xFF);
362 					}
363 				}
364 
365 				/* find vertex in this surface and if we can't find it there create it */
366 				vertexCombinationHash = PicoFindVertexCombinationInHashTable( hashTable, xyz, normal, st, color );
367 
368 				if (vertexCombinationHash)
369 				{
370 					/* found an existing one */
371 					PicoSetSurfaceIndex( picoSurface, (i * 3 + j ), vertexCombinationHash->index );
372 				}
373 				else
374 				{
375 					/* it is a new one */
376 					vertexCombinationHash = PicoAddVertexCombinationToHashTable( hashTable, xyz, normal, st, color, (picoIndex_t) numverts );
377 
378 					if (vertexCombinationHash == NULL)
379 					{
380 						_pico_printf( PICO_ERROR, "Unable to allocate hash bucket entry table" );
381 						PicoFreeVertexCombinationHashTable( hashTable );
382 						PicoFreeModel( picoModel );
383 						lwFreeObject( obj );
384 						return NULL;
385 					}
386 
387 					/* add the vertex to this surface */
388 					PicoSetSurfaceXYZ( picoSurface, numverts, xyz );
389 
390 					/* set dummy normal */
391 					PicoSetSurfaceNormal( picoSurface, numverts, normal );
392 
393 					/* set color */
394 					PicoSetSurfaceColor( picoSurface, 0, numverts, color );
395 
396 					/* set st coords */
397 					PicoSetSurfaceST( picoSurface, 0, numverts, st );
398 
399 					/* set index */
400 					PicoSetSurfaceIndex( picoSurface, (i * 3 + j ), (picoIndex_t) numverts );
401 
402 					numverts++;
403 				}
404 			}
405 		}
406 
407 		/* free the hashtable */
408 		PicoFreeVertexCombinationHashTable( hashTable );
409 
410 		/* get next surface */
411 		surface = surface->next;
412 	}
413 
414 #ifdef DEBUG_PM_LWO
415 	load_start = convert_finish = clock();
416 #endif
417 
418 	lwFreeObject( obj );
419 
420 #ifdef DEBUG_PM_LWO
421 	load_finish = clock();
422 	load_elapsed += (double)(load_finish - load_start) / CLOCKS_PER_SEC;
423 	convert_elapsed = (double)(convert_finish - convert_start) / CLOCKS_PER_SEC;
424 	_pico_printf( PICO_NORMAL, "Loaded model in in %-.2f second(s) (loading: %-.2fs converting: %-.2fs)\n", load_elapsed + convert_elapsed, load_elapsed, convert_elapsed  );
425 #endif
426 
427 	/* return the new pico model */
428 	return picoModel;
429 }
430 
431 /* pico file format module definition */
432 const picoModule_t picoModuleLWO =
433 {
434 	"1.0",						/* module version string */
435 	"LightWave Object",			/* module display name */
436 	"Arnout van Meer",			/* author's name */
437 	"2003 Arnout van Meer, 2000 Ernie Wright",		/* module copyright */
438 	{
439 		"lwo", NULL, NULL, NULL	/* default extensions to use */
440 	},
441 	_lwo_canload,				/* validation routine */
442 	_lwo_load,					/* load routine */
443 	 NULL,						/* save validation routine */
444 	 NULL						/* save routine */
445 };
446