mirror of
https://github.com/kevinbentley/Descent3.git
synced 2025-01-22 19:55:23 +00:00
862 lines
20 KiB
C++
862 lines
20 KiB
C++
/*
|
|
* Descent 3
|
|
* Copyright (C) 2024 Parallax Software
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
--- HISTORICAL COMMENTS FOLLOW ---
|
|
|
|
* $Logfile: /DescentIII/Main/editor/Read3ds.cpp $
|
|
* $Revision: 1.1.1.1 $
|
|
* $Date: 2003-08-26 03:57:38 $
|
|
* $Author: kevinb $
|
|
*
|
|
* Code to read .p3d files generated in 3DS Max
|
|
*
|
|
* $Log: not supported by cvs2svn $
|
|
*
|
|
* 26 10/15/99 12:26p Matt
|
|
* Added error checking for too many verts & faces when importing a room.
|
|
* Also, now allow more than the max number of faces, as long as there is
|
|
* a valid number after combining faces.
|
|
*
|
|
* 25 4/13/99 11:23a Jason
|
|
* check for degenerate faces on import
|
|
*
|
|
* 24 2/25/99 10:35a Jason
|
|
* added removal of redundant verts
|
|
*
|
|
* 23 2/04/99 4:40p Matt
|
|
* Added some error checking
|
|
*
|
|
* 22 1/21/99 11:15p Jeff
|
|
* pulled out some structs and defines from header files and moved them
|
|
* into seperate header files so that multiplayer dlls don't require major
|
|
* game headers, just those new headers. Side effect is a shorter build
|
|
* time. Also cleaned up some header file #includes that weren't needed.
|
|
* This affected polymodel.h, object.h, player.h, vecmat.h, room.h,
|
|
* manage.h and multi.h
|
|
*
|
|
* 21 12/22/98 2:03p Matt
|
|
* Added room names, and made rooms not compress so that room numbers are
|
|
* suitable for persistant uses.
|
|
*
|
|
* 20 5/01/98 5:55p Jason
|
|
* made combine faces much stricter
|
|
*
|
|
* 19 4/02/98 12:23p Jason
|
|
* trimmed some fat from our structures
|
|
*
|
|
* 18 3/31/98 3:49p Jason
|
|
* added memory lib
|
|
*
|
|
* 17 2/02/98 5:14p Matt
|
|
* Check for bad normals when importing room
|
|
*
|
|
* 16 1/22/98 2:56p Brent
|
|
* Define default textures to unassigned faces
|
|
*
|
|
* 15 1/19/98 2:55p Jason
|
|
* added the ability to have the importer keep the textures on the faces
|
|
* if they are already present in memory
|
|
*
|
|
* 14 12/23/97 11:06a Samir
|
|
* Added pserror.h
|
|
*
|
|
* 13 12/10/97 5:20p Jason
|
|
* set alphas to 1 when importing rooms
|
|
*
|
|
* 12 12/10/97 4:26p Jason
|
|
* don't assign default UVs to room
|
|
*
|
|
* 11 8/21/97 5:57p Matt
|
|
* Use new & modified functions from erooms.cpp
|
|
*
|
|
* 10 8/01/97 3:16p Chris
|
|
*
|
|
* 9 7/21/97 12:11p Matt
|
|
* Fixed stupid bug in concavity check
|
|
*
|
|
* 8 7/21/97 11:39a Jason
|
|
* checked in for matt to debug
|
|
*
|
|
* 7 7/17/97 11:13a Jason
|
|
* fixed bug with reading in rooms - somehow it got broken
|
|
*
|
|
* 7 7/17/97 10:51a Jason
|
|
* fixed bug with normals
|
|
*
|
|
* $NoKeywords: $
|
|
*/
|
|
|
|
#include "read3ds.h"
|
|
#include "cfile.h"
|
|
#include "room.h"
|
|
#include "erooms.h"
|
|
#include "gametexture.h"
|
|
#include "ddio.h"
|
|
#include "pserror.h"
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include "mem.h"
|
|
#include "vecmat.h"
|
|
|
|
|
|
// 3ds MAX id's
|
|
#define ID_3DS_MODIFIED 0xbeef
|
|
#define ID_OBJ_PROPS 0xdead
|
|
#define ID_SPLINE_PATH 0x8001
|
|
#define ID_3DSM 0xcfd0
|
|
#define ID_3DS 0x4d4d
|
|
#define ID_OBJECT 0x4000
|
|
#define ID_UNKNOWN 0x3d3d
|
|
#define ID_TRI_MESH 0x4100
|
|
#define ID_VERTLIST 0x4110
|
|
#define ID_FACELIST 0x4120
|
|
#define ID_MAT_APP 0x4130
|
|
#define ID_VERTEX_MAPPING 0x4140
|
|
#define ID_SMOOTH 0x4150
|
|
#define ID_MATRIX 0x4160
|
|
#define ID_MATERIAL 0xafff
|
|
#define ID_MAT_NAME 0xa000
|
|
#define ID_MAT_DSIDED 0xa081
|
|
#define ID_MAT_DIFFUSE_COLOR 0xa020
|
|
#define ID_MAT_TEXTURE 0xa200
|
|
#define ID_DIRECT_LIGHT 0x4600
|
|
#define OBJECT_NODE_TAG 0xB002
|
|
|
|
char Reading_properties[255];
|
|
|
|
#define MAX_MATERIALS 100
|
|
|
|
struct material
|
|
{
|
|
char name[PAGENAME_LEN];
|
|
int texhandle;
|
|
};
|
|
|
|
int Num_materials=0;
|
|
material Materials[MAX_MATERIALS];
|
|
|
|
struct reading_face
|
|
{
|
|
uint8_t flags; // flags for this face (see above)
|
|
int16_t portal_num; // which portal this face is part of, or -1 if none
|
|
uint8_t num_verts; // how many vertices in this face
|
|
int16_t face_verts[MAX_VERTS_PER_FACE]; // index into list of vertices for this face
|
|
roomUVL face_uvls[MAX_VERTS_PER_FACE]; // index into list of uvls for this face
|
|
vector normal; // the surface normal of this face
|
|
int16_t tmap; // texture numbers for this face
|
|
};
|
|
|
|
struct reading_room {
|
|
char name[PAGENAME_LEN];
|
|
int flags; // various room flags
|
|
int num_faces; // how many poygons in this room
|
|
int num_portals; // how many connections in this room
|
|
int num_verts; // how many verts in the room
|
|
reading_face *faces; // pointer to list of faces
|
|
vector *verts; // array of vertices for this room
|
|
int objects; // index of first object in this room
|
|
float static_light; // the amount of light in this room
|
|
};
|
|
|
|
|
|
|
|
reading_room Reading_room; // the global that we use to keep a temp copy of the room while
|
|
// we're reading it in
|
|
|
|
int CombineFaces (reading_face *,reading_face *,reading_face *);
|
|
|
|
// Our nest level
|
|
int Nest_level=0;
|
|
|
|
extern void AssignDefaultUVsToRoom(room *rp);
|
|
|
|
int DeleteUnusedRoomVerts(room *);
|
|
int RemoveDuplicatePoints(room *);
|
|
int RemoveDuplicateFacePoints(room *);
|
|
|
|
#define MAX_READING_ROOM_FACES (MAX_FACES_PER_ROOM * 2)
|
|
|
|
// Opens and reads a 3dsmax file for our rooms. Allocs a room to carry the data
|
|
// Returns the index into the Rooms[] array if successful
|
|
// Return -1 on fail
|
|
int Read3DSMaxFile(char *filename)
|
|
{
|
|
uint16_t id;
|
|
int len;
|
|
CFILE *fp;
|
|
int i;
|
|
|
|
fp=(CFILE *)cfopen (filename,"rb");
|
|
if (!fp)
|
|
{
|
|
mprintf(0,"Couldn't open 3dsmax file %s!\n",filename);
|
|
return -1;
|
|
}
|
|
|
|
Nest_level=0;
|
|
|
|
// Alloc space for reading stuff in
|
|
Reading_room.faces=(reading_face *)mem_malloc (MAX_READING_ROOM_FACES*sizeof(reading_face));
|
|
Reading_room.verts=(vector *)mem_malloc (MAX_VERTS_PER_ROOM*sizeof(vector));
|
|
|
|
Reading_room.num_faces=0;
|
|
Reading_room.num_verts=0;
|
|
|
|
id=cf_ReadShort (fp);
|
|
len=cf_ReadInt(fp);
|
|
|
|
Num_materials=0;
|
|
|
|
if (id == ID_3DS_MODIFIED)
|
|
Parse3DSMaxChunk (fp, len-6);
|
|
else
|
|
{
|
|
mprintf(0,"This file is not a 3ds max file!\n");
|
|
cfclose (fp);
|
|
return -1;
|
|
}
|
|
cfclose (fp);
|
|
|
|
if ((Reading_room.num_verts == 0) || (Reading_room.num_faces == 0)) {
|
|
OutrageMessageBox("The imported room has %d verts and %d faces. Aborting import.",Reading_room.num_faces,Reading_room.num_faces);
|
|
return -1;
|
|
}
|
|
|
|
if (Reading_room.num_verts > MAX_VERTS_PER_ROOM) {
|
|
OutrageMessageBox("The imported room has %d verts. The limit is %d. Aborting import.",Reading_room.num_verts,MAX_VERTS_PER_ROOM);
|
|
return -1;
|
|
}
|
|
|
|
if (Reading_room.num_faces > MAX_READING_ROOM_FACES) {
|
|
OutrageMessageBox("The imported room has %d faces. The limit is %d. Aborting import.",Reading_room.num_faces,MAX_READING_ROOM_FACES);
|
|
return -1;
|
|
}
|
|
|
|
if (Reading_room.num_faces > MAX_FACES_PER_ROOM) {
|
|
OutrageMessageBox("The imported room has %d faces. The limit after combining faces is %d.\n\nIf there are too many faces after combining, this room will not be imported.",Reading_room.num_faces,MAX_FACES_PER_ROOM);
|
|
}
|
|
|
|
// Convert our points to left handed space
|
|
for (i=0;i<Reading_room.num_verts;i++)
|
|
ConvertHandiness (&Reading_room.verts[i]);
|
|
|
|
// calculate normals
|
|
int bad_normals = 0;
|
|
for (i=0;i<Reading_room.num_faces;i++)
|
|
{
|
|
reading_room *rp=&Reading_room;
|
|
reading_face *mfp=&rp->faces[i];
|
|
//vm_GetNormal(&Reading_room.faces[i].normal,&rp->verts[mfp->face_verts[0]],&rp->verts[mfp->face_verts[1]],&rp->verts[mfp->face_verts[2]]);
|
|
if (! ComputeNormal(&Reading_room.faces[i].normal,Reading_room.faces[i].num_verts,mfp->face_verts,rp->verts)) {
|
|
mprintf(1,"Warning: Low precision normal for face %d\n",i);
|
|
bad_normals++;
|
|
}
|
|
}
|
|
if (bad_normals) {
|
|
OutrageMessageBox("Warning: The loaded room has %d faces with bad normals -- see the mono screen for details.\n\n"
|
|
"Coplanar faces in this room have NOT been combined.\n\n"
|
|
"It is STRONGLY recommended that you fix this room before using it.",bad_normals);
|
|
goto skip_combine;
|
|
}
|
|
|
|
// Now make a copy of the relevant Reading_room data into our destination
|
|
int t;
|
|
reading_face destface;
|
|
|
|
mprintf(0,"Combining faces, please wait...\n");
|
|
|
|
TryAgain:
|
|
|
|
for (i=0;i<Reading_room.num_faces;i++)
|
|
{
|
|
reading_face *a=&Reading_room.faces[i];
|
|
|
|
for (t=0;t<Reading_room.num_faces;t++)
|
|
{
|
|
|
|
reading_face *b=&Reading_room.faces[t];
|
|
|
|
if (a==b)
|
|
continue;
|
|
|
|
int retval=CombineFaces (&destface,a,b);
|
|
if (retval)
|
|
{
|
|
// Copy the new face in the place of face a,
|
|
// remove face b and then start over!
|
|
|
|
|
|
Reading_room.faces[i]=destface;
|
|
int k;
|
|
for (k=t;k<Reading_room.num_faces-1;k++)
|
|
Reading_room.faces[k]=Reading_room.faces[k+1];
|
|
|
|
Reading_room.num_faces--;
|
|
|
|
goto TryAgain;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
skip_combine:;
|
|
|
|
mprintf(0,"Total faces=%d\n",Reading_room.num_faces);
|
|
|
|
if (Reading_room.num_faces > MAX_FACES_PER_ROOM) {
|
|
OutrageMessageBox("The imported room has %d faces. The limit is %d. Aborting import.",Reading_room.num_faces,MAX_FACES_PER_ROOM);
|
|
return -1;
|
|
}
|
|
|
|
mprintf(0,"Trying to allocate a room for %d verts, %d faces!\n",Reading_room.num_verts,Reading_room.num_faces);
|
|
// int n=AllocRoom (Reading_room.num_verts,Reading_room.num_faces);
|
|
room *rp = CreateNewRoom(Reading_room.num_verts,Reading_room.num_faces,1);
|
|
|
|
if (rp != NULL)
|
|
{
|
|
for (i=0;i<Reading_room.num_verts;i++)
|
|
rp->verts[i]=Reading_room.verts[i];
|
|
|
|
for (i=0;i<Reading_room.num_faces;i++)
|
|
{
|
|
InitRoomFace(&rp->faces[i],Reading_room.faces[i].num_verts);
|
|
|
|
rp->faces[i].normal=Reading_room.faces[i].normal;
|
|
rp->faces[i].num_verts=Reading_room.faces[i].num_verts;
|
|
|
|
rp->faces[i].tmap=Reading_room.faces[i].tmap;
|
|
|
|
for (t=0;t<Reading_room.faces[i].num_verts;t++)
|
|
{
|
|
rp->faces[i].face_verts[t]=Reading_room.faces[i].face_verts[t];
|
|
rp->faces[i].face_uvls[t]=Reading_room.faces[i].face_uvls[t];
|
|
}
|
|
}
|
|
|
|
// Reset UV's
|
|
|
|
//AssignDefaultUVsToRoom (rp);
|
|
|
|
// Remove redundant verts
|
|
DeleteUnusedRoomVerts(rp);
|
|
RemoveDuplicatePoints(rp);
|
|
RemoveDuplicateFacePoints(rp);
|
|
|
|
char name[255];
|
|
char path[255];
|
|
char extension[255];
|
|
char roomname[255];
|
|
|
|
|
|
|
|
// Find a unique name for this room
|
|
|
|
ddio_SplitPath (filename,path,name,extension);
|
|
int done=0;
|
|
int count=1;
|
|
|
|
sprintf (roomname,"%s.ORF",name);
|
|
|
|
while (!done)
|
|
{
|
|
int val=FindRoomName (roomname);
|
|
if (val==-1)
|
|
{
|
|
done=1;
|
|
continue;
|
|
}
|
|
count++;
|
|
sprintf (roomname,"%s%d.ORF",name,count);
|
|
}
|
|
|
|
ASSERT(rp->name == NULL);
|
|
rp->name = (char *) mem_malloc(strlen(roomname)+1);
|
|
strcpy (rp->name,roomname);
|
|
|
|
// Save it out to disk (locally)
|
|
ddio_MakePath (name,LocalRoomsDir,roomname,NULL);
|
|
SaveRoom (ROOMNUM(rp),name);
|
|
}
|
|
|
|
|
|
// Free our verts
|
|
|
|
if (Reading_room.faces)
|
|
mem_free (Reading_room.faces);
|
|
if (Reading_room.verts)
|
|
mem_free (Reading_room.verts);
|
|
|
|
return ROOMNUM(rp);
|
|
}
|
|
|
|
// Converts the 3dsmax coordinate space into our left-handed coordinate space
|
|
void ConvertHandiness( vector * v )
|
|
{
|
|
vector v1 = *v;
|
|
v->x = -1.0f*v1.x;
|
|
v->y = v1.z;
|
|
v->z = -1.0f*v1.y;
|
|
}
|
|
|
|
#define skip(f,n) cfseek(f,n,SEEK_CUR)
|
|
// Parses a chunk of a 3dsmax file - this function calls itself
|
|
void Parse3DSMaxChunk (CFILE *fp, int size)
|
|
{
|
|
uint16_t id;
|
|
int len;
|
|
int level=Nest_level;
|
|
int i;
|
|
float scale_factor = 0.0254f; // 0.0254 inches/meter
|
|
|
|
Nest_level++;
|
|
|
|
while (size && !cfeof(fp))
|
|
{
|
|
|
|
id=cf_ReadShort(fp);
|
|
len=cf_ReadInt(fp);
|
|
|
|
if (size<0)
|
|
{
|
|
mprintf(0,"%d:chunk error\n",level);
|
|
exit (1);
|
|
}
|
|
|
|
switch (id)
|
|
{
|
|
case ID_UNKNOWN:
|
|
Parse3DSMaxChunk(fp,len-6);
|
|
break;
|
|
|
|
case ID_MAT_NAME:
|
|
{
|
|
char material_name[PAGENAME_LEN];
|
|
|
|
cf_ReadString(material_name,PAGENAME_LEN,fp);
|
|
|
|
strcpy (Materials[Num_materials].name,material_name);
|
|
Num_materials++;
|
|
|
|
ASSERT (Num_materials<MAX_MATERIALS);
|
|
break;
|
|
}
|
|
|
|
case ID_MATERIAL:
|
|
{
|
|
Parse3DSMaxChunk(fp,len-6);
|
|
break;
|
|
}
|
|
|
|
case ID_MAT_TEXTURE:
|
|
{
|
|
int i;
|
|
char texture_name [PAGENAME_LEN];
|
|
|
|
// Read in Unknown field
|
|
for (i=0; i<6; i++ )
|
|
cf_ReadByte (fp);
|
|
|
|
cf_ReadShort(fp);
|
|
|
|
// Read in Unknown field
|
|
for (i=0; i<6; i++ )
|
|
cf_ReadByte (fp);
|
|
|
|
cf_ReadString(texture_name,PAGENAME_LEN,fp);
|
|
|
|
// Find the texture that has this bitmap as a name
|
|
|
|
int texlen=strlen(texture_name);
|
|
|
|
texture_name[texlen-4]=0;
|
|
strcat( texture_name, ".OGF" );
|
|
|
|
int ret=FindTextureBitmapName(texture_name);
|
|
if (ret==-1)
|
|
{
|
|
mprintf(0,"Couldn't find bitmap %s!\n",texture_name);
|
|
ret=GetNextTexture(0);
|
|
}
|
|
|
|
Materials[Num_materials-1].texhandle=ret;
|
|
|
|
skip(fp,len-6-6-2-6-(strlen(texture_name)+1));
|
|
|
|
break;
|
|
}
|
|
case ID_MAT_APP:
|
|
{
|
|
char material_name[PAGENAME_LEN];
|
|
int16_t n_faces,face;
|
|
int done=0;
|
|
int texnum;
|
|
|
|
cf_ReadString(material_name,PAGENAME_LEN,fp);
|
|
|
|
n_faces=cf_ReadShort (fp);
|
|
|
|
for (int i=0;i<Num_materials && !done;i++)
|
|
{
|
|
if (!stricmp (Materials[i].name,material_name))
|
|
{
|
|
texnum=Materials[i].texhandle;
|
|
done=1;
|
|
}
|
|
}
|
|
|
|
if (!done)
|
|
{
|
|
mprintf(0,"Couldn't find material named %s!\n",material_name);
|
|
texnum=GetNextTexture(0);
|
|
}
|
|
|
|
while (n_faces--)
|
|
{
|
|
face=cf_ReadShort(fp);
|
|
|
|
Reading_room.faces[face].tmap=texnum;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case ID_OBJECT:
|
|
{
|
|
cf_ReadString (Reading_room.name,PAGENAME_LEN,fp);
|
|
Parse3DSMaxChunk(fp,len-6-(strlen(Reading_room.name)+1));
|
|
break;
|
|
}
|
|
// Special properties for this object/room
|
|
case ID_OBJ_PROPS:
|
|
{
|
|
cf_ReadString (Reading_properties,255,fp);
|
|
// Skip next two ints
|
|
cf_ReadInt(fp);
|
|
cf_ReadInt(fp);
|
|
|
|
Parse3DSMaxChunk(fp, len-6-4-4-(strlen(Reading_properties)+1));
|
|
break;
|
|
}
|
|
|
|
case ID_TRI_MESH:
|
|
mprintf(0,"Found 3dsmax TRI_MESH chunk!\n");
|
|
|
|
Parse3DSMaxChunk(fp,len-6);
|
|
break;
|
|
|
|
// Vertex list
|
|
case ID_VERTLIST:
|
|
{
|
|
uint16_t num_verts=cf_ReadShort(fp);
|
|
int i;
|
|
|
|
if (num_verts > MAX_VERTS_PER_ROOM)
|
|
return;
|
|
|
|
mprintf(0,"Reading in %d verts from room!\n",num_verts);
|
|
|
|
// Make room for these verts
|
|
Reading_room.num_verts=num_verts;
|
|
|
|
for (i=0;i<num_verts;i++)
|
|
{
|
|
Reading_room.verts[i].x=cf_ReadFloat(fp);
|
|
Reading_room.verts[i].y=cf_ReadFloat(fp);
|
|
Reading_room.verts[i].z=cf_ReadFloat(fp);
|
|
|
|
Reading_room.verts[i]*=scale_factor;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case ID_FACELIST:
|
|
{
|
|
uint16_t num_faces=cf_ReadShort(fp);
|
|
uint16_t a,b,c,flags;
|
|
int i,t, j, this_size;
|
|
|
|
if (num_faces > MAX_READING_ROOM_FACES)
|
|
return;
|
|
|
|
mprintf(0,"Reading in %d faces!\n",num_faces);
|
|
|
|
Reading_room.num_faces=num_faces;
|
|
ASSERT (Reading_room.faces!=NULL);
|
|
|
|
for (i=0;i<num_faces;i++)
|
|
{
|
|
// a,b, and c are indices into the list of vertices for this room
|
|
Reading_room.faces[i].num_verts=3;
|
|
|
|
a = cf_ReadShort(fp);
|
|
b = cf_ReadShort(fp);
|
|
c = cf_ReadShort(fp);
|
|
|
|
flags = cf_ReadShort(fp);
|
|
|
|
// Set our pointers accordingly
|
|
// We must reverse the ordering of the verts because
|
|
// 3ds has a right handed coordinate system
|
|
Reading_room.faces[i].face_verts[0]=c;
|
|
Reading_room.faces[i].face_verts[1]=b;
|
|
Reading_room.faces[i].face_verts[2]=a;
|
|
|
|
Reading_room.faces[i].tmap=GetNextTexture(0);
|
|
|
|
Reading_room.faces[i].flags=flags;
|
|
|
|
// Read UVs
|
|
|
|
float u[3],v[3];
|
|
for (t=0; t<3; t++ )
|
|
{
|
|
u[t]=cf_ReadFloat (fp);
|
|
v[t]=cf_ReadFloat (fp);
|
|
}
|
|
|
|
for (j = 0; j < 3; j++)
|
|
{
|
|
Reading_room.faces[i].face_uvls[j].u = u[2-j];
|
|
Reading_room.faces[i].face_uvls[j].v = -v[2-j];
|
|
Reading_room.faces[i].face_uvls[j].alpha = 255;
|
|
|
|
}
|
|
}
|
|
|
|
//get whatever is left in this segment
|
|
this_size = num_faces * ((4 * sizeof(int16_t)) + (6*sizeof(float)));
|
|
if (len-6-this_size )
|
|
Parse3DSMaxChunk(fp,len-6-2-this_size);
|
|
|
|
|
|
break;
|
|
}
|
|
|
|
case ID_MATRIX:
|
|
{
|
|
float room_matrix[12];
|
|
int16_t i;
|
|
for (i=0; i<12; i++ )
|
|
room_matrix[i] = cf_ReadFloat(fp);
|
|
break;
|
|
}
|
|
|
|
|
|
case OBJECT_NODE_TAG:
|
|
{
|
|
//printf( "=================== OBJECT_NODE_TAG! =======================\n" );
|
|
|
|
Parse3DSMaxChunk(fp,len-6);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
// Skip this stuff
|
|
for (i=0;i<len-6;i++)
|
|
cf_ReadByte (fp);
|
|
break;
|
|
|
|
|
|
}
|
|
size -= len;
|
|
}
|
|
|
|
Nest_level--;
|
|
}
|
|
|
|
int next_vertex( reading_face *f, int v )
|
|
{
|
|
return f->face_verts[(v+1)%f->num_verts];
|
|
}
|
|
|
|
int this_vertex( reading_face *f, int v )
|
|
{
|
|
return f->face_verts[v];
|
|
}
|
|
|
|
int compute_faces_mapping( reading_face * f, vector *out_norm, float *out_d )
|
|
{
|
|
int i;
|
|
float ut, vt, len;
|
|
vector point, u, v;
|
|
vector normal;
|
|
|
|
vm_MakeZero(&point );
|
|
vm_MakeZero( &normal );
|
|
|
|
ut = vt = 0.0f;
|
|
|
|
for (i=0; i<f->num_verts; i++ )
|
|
{
|
|
u.x = f->face_uvls[i].u;
|
|
u.y = f->face_uvls[i].v;
|
|
u.z = 1.0f;
|
|
|
|
v.x = f->face_uvls[(i+1) % f->num_verts].u;
|
|
v.y = f->face_uvls[(i+1) % f->num_verts].v;
|
|
v.z = 1.0f;
|
|
|
|
ut += (float)fabs( u.x - v.x );
|
|
vt += (float)fabs( u.y - v.y );
|
|
|
|
normal.x += (u.y-v.y) * (u.z + v.z);
|
|
normal.y += (u.z-v.z) * (u.x + v.x);
|
|
normal.z += (u.x-v.x) * (u.y + v.y);
|
|
point+=u;
|
|
}
|
|
|
|
len = vm_GetMagnitude( &normal );
|
|
|
|
out_norm->x = normal.x / len;
|
|
out_norm->y = normal.y / len;
|
|
out_norm->z = normal.z / len;
|
|
len *= f->num_verts;
|
|
*out_d = (vm_DotProduct( &point, &normal ) / len);
|
|
point.x /= f->num_verts;
|
|
point.y /= f->num_verts;
|
|
point.z /= f->num_verts;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
// Returns 1 if the uv's match
|
|
int uvs_match( reading_face * a, int va, reading_face * b, int vb )
|
|
{
|
|
int f1, f2, flag;
|
|
vector n1, n2;
|
|
float d1, d2;
|
|
float cosTheta;
|
|
float u_err, v_err;
|
|
|
|
u_err = (float)fabs( (a->face_uvls[va].u-b->face_uvls[vb].u) / ((a->face_uvls[va].u+b->face_uvls[vb].u)/2.0f) );
|
|
v_err = (float)fabs( (a->face_uvls[va].v-b->face_uvls[vb].v) / ((a->face_uvls[va].v+b->face_uvls[vb].v)/2.0f) );
|
|
|
|
if ( u_err+v_err > 0.00001f ) return 0;
|
|
|
|
f1 = compute_faces_mapping( a, &n1, &d1 );
|
|
f2 = compute_faces_mapping( b, &n2, &d2 );
|
|
|
|
if ( f1 || f2 ) return 0;
|
|
|
|
cosTheta = vm_DotProduct( &n1, &n2 );
|
|
|
|
if ( (cosTheta < 0.99f) || (fabs( d2-d1 ) > 0.0001f) ) {
|
|
flag = 0;
|
|
} else {
|
|
flag = 1;
|
|
}
|
|
|
|
|
|
return flag;
|
|
}
|
|
|
|
#define MAX_POINT_DISTANCE_FROM_PLANE .1
|
|
|
|
int CombineFaces( reading_face *dest,reading_face * a, reading_face * b )
|
|
{
|
|
int starta, startb, i;
|
|
int va;
|
|
|
|
if (! NormalsAreSame(&b->normal,&a->normal))
|
|
return 0;
|
|
|
|
if (a->tmap!=b->tmap)
|
|
return 0;
|
|
|
|
|
|
ASSERT (a->num_verts > 2 );
|
|
ASSERT (b->num_verts > 2 );
|
|
|
|
// Compare points to plane
|
|
vector vec=Reading_room.verts[a->face_verts[0]];
|
|
vector norm=a->normal;
|
|
float plane_dist=-(vec.x*norm.x+vec.y*norm.y+vec.z*norm.z);
|
|
|
|
for (i=0;i<b->num_verts;i++)
|
|
{
|
|
vec=Reading_room.verts[b->face_verts[i]];
|
|
float dist = vec.x*norm.x+vec.y*norm.y+vec.z*norm.z+plane_dist;
|
|
if (fabs(dist)>MAX_POINT_DISTANCE_FROM_PLANE)
|
|
return 0;
|
|
}
|
|
|
|
// Go through each vertex and get a match
|
|
|
|
for (starta=0; starta<a->num_verts; starta++ )
|
|
{
|
|
for (startb=0; startb<b->num_verts; startb++ )
|
|
{
|
|
if ( (this_vertex(a,starta)==next_vertex(b,startb)) && (next_vertex(a,starta)==this_vertex(b,startb)) &&
|
|
uvs_match(a,starta,b,(startb+1)%b->num_verts) && uvs_match(a,(starta+1)%a->num_verts,b,startb) )
|
|
{
|
|
//MATCH!!!!!!!!
|
|
|
|
dest->num_verts = 0;
|
|
dest->flags = a->flags;
|
|
dest->normal = a->normal; //normal of this face
|
|
dest->tmap = a->tmap;
|
|
|
|
for (i=1; i<a->num_verts; i++ )
|
|
{
|
|
ASSERT(dest->num_verts < MAX_VERTS_PER_FACE);
|
|
dest->face_verts[dest->num_verts] = a->face_verts[(starta+i)%a->num_verts];
|
|
dest->face_uvls[dest->num_verts] = a->face_uvls[(starta+i)%a->num_verts];
|
|
va = dest->face_verts[dest->num_verts];
|
|
dest->num_verts++;
|
|
}
|
|
|
|
if ( (va==b->face_verts[(startb+2)%b->num_verts]))
|
|
mprintf(0, "WARNING!!! Faces were combined that caused the loss of a vertex!\n");
|
|
|
|
for (i=1; i<b->num_verts; i++ )
|
|
{
|
|
ASSERT(dest->num_verts < MAX_VERTS_PER_FACE );
|
|
if ( (i==1) && (va==b->face_verts[(startb+i+1)%b->num_verts]))
|
|
continue;
|
|
else if ( (i==2) && (va==b->face_verts[(startb+i)%b->num_verts]))
|
|
continue;
|
|
else
|
|
{
|
|
dest->face_verts[dest->num_verts] = b->face_verts[(startb+i)%b->num_verts];
|
|
dest->face_uvls[dest->num_verts] = b->face_uvls[(startb+i)%b->num_verts];
|
|
dest->num_verts++;
|
|
|
|
}
|
|
}
|
|
ASSERT( dest->num_verts > 2 );
|
|
|
|
if ((CheckFaceConcavity (dest->num_verts,dest->face_verts,&dest->normal,Reading_room.verts))>=0)
|
|
return 0;
|
|
|
|
// Now check for degenerate face
|
|
for (int v=0;v<dest->num_verts;v++)
|
|
{
|
|
if (dest->face_verts[v] == dest->face_verts[(v+2)%dest->num_verts])
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|