librw/src/geometry.cpp

1012 lines
25 KiB
C++
Raw Normal View History

2014-12-18 17:26:57 +01:00
#include <cstdio>
#include <cstdlib>
#include <cstring>
2014-12-19 22:58:10 +01:00
#include <cassert>
2015-12-19 17:05:39 +01:00
#include <cmath>
2014-12-18 17:26:57 +01:00
#include "rwbase.h"
#include "rwerror.h"
2016-06-16 14:08:09 +02:00
#include "rwplg.h"
2015-07-11 23:48:11 +02:00
#include "rwpipeline.h"
2014-12-23 15:59:14 +01:00
#include "rwobjects.h"
2017-08-24 15:10:34 +02:00
#include "rwengine.h"
2014-12-18 17:26:57 +01:00
2017-08-24 15:10:34 +02:00
#define PLUGIN_ID ID_GEOMETRY
namespace rw {
2014-12-18 17:26:57 +01:00
PluginList Geometry::s_plglist = { sizeof(Geometry), sizeof(Geometry), nil, nil };
PluginList Material::s_plglist = { sizeof(Material), sizeof(Material), nil, nil };
2017-08-25 14:06:53 +02:00
static SurfaceProperties defaultSurfaceProps = { 1.0f, 1.0f, 1.0f };
2017-03-16 11:42:59 +01:00
2017-08-25 14:06:53 +02:00
// We allocate twice because we have to allocate the data separately for uninstancing
Geometry*
Geometry::create(int32 numVerts, int32 numTris, uint32 flags)
2014-12-18 17:26:57 +01:00
{
2017-08-24 15:10:34 +02:00
Geometry *geo = (Geometry*)rwMalloc(s_plglist.size, MEMDUR_EVENT | ID_GEOMETRY);
if(geo == nil){
RWERROR((ERR_ALLOC, s_plglist.size));
return nil;
}
2016-01-13 08:58:15 +01:00
geo->object.init(Geometry::ID, 0);
2017-03-16 11:42:59 +01:00
geo->flags = flags & 0xFF00FFFF;
geo->numTexCoordSets = (flags & 0xFF0000) >> 16;
if(geo->numTexCoordSets == 0)
2017-03-16 11:42:59 +01:00
geo->numTexCoordSets = (geo->flags & TEXTURED) ? 1 :
(geo->flags & TEXTURED2) ? 2 : 0;
geo->numTriangles = numTris;
geo->numVertices = numVerts;
geo->colors = nil;
2017-08-25 14:06:53 +02:00
for(int32 i = 0; i < 8; i++)
geo->texCoords[i] = nil;
geo->triangles = nil;
2017-08-25 14:06:53 +02:00
// Allocate all attributes at once. The triangle pointer
// will hold the first address (even when there are no triangles)
// so we can free easily.
if(!(geo->flags & NATIVE)){
int32 sz = geo->numTriangles*sizeof(Triangle);
2017-03-16 11:42:59 +01:00
if(geo->flags & PRELIT)
2017-08-25 14:06:53 +02:00
sz += geo->numVertices*sizeof(RGBA);
sz += geo->numTexCoordSets*geo->numVertices*sizeof(TexCoords);
uint8 *data = (uint8*)rwNew(sz, MEMDUR_EVENT | ID_GEOMETRY);
geo->triangles = (Triangle*)data;
data += geo->numTriangles*sizeof(Triangle);
if(geo->flags & PRELIT && geo->numVertices){
geo->colors = (RGBA*)data;
data += geo->numVertices*sizeof(RGBA);
}
if(geo->numVertices)
for(int32 i = 0; i < geo->numTexCoordSets; i++){
geo->texCoords[i] = (TexCoords*)data;
data += geo->numVertices*sizeof(TexCoords);
}
// init triangles
for(int32 i = 0; i < geo->numTriangles; i++)
geo->triangles[i].matId = 0xFFFF;
2014-12-19 22:58:10 +01:00
}
2017-08-25 14:06:53 +02:00
geo->numMorphTargets = 0;
geo->morphTargets = nil;
geo->addMorphTargets(1);
2017-03-16 11:42:59 +01:00
geo->matList.init();
geo->meshHeader = nil;
geo->instData = nil;
geo->refCount = 1;
2014-12-18 17:26:57 +01:00
s_plglist.construct(geo);
return geo;
2014-12-18 17:26:57 +01:00
}
2014-12-20 11:38:27 +01:00
void
Geometry::destroy(void)
2014-12-20 11:38:27 +01:00
{
this->refCount--;
2016-01-13 08:58:15 +01:00
if(this->refCount <= 0){
s_plglist.destruct(this);
2017-08-25 14:06:53 +02:00
// Also frees colors and tex coords
rwFree(this->triangles);
// Also frees their data
rwFree(this->morphTargets);
// Also frees indices
rwFree(this->meshHeader);
2017-03-16 11:42:59 +01:00
this->matList.deinit();
2017-08-24 15:10:34 +02:00
rwFree(this);
}
2014-12-18 17:26:57 +01:00
}
struct GeoStreamData
{
uint32 flags;
int32 numTriangles;
int32 numVertices;
int32 numMorphTargets;
};
Geometry*
Geometry::streamRead(Stream *stream)
2014-12-18 17:26:57 +01:00
{
uint32 version;
GeoStreamData buf;
2017-03-16 11:42:59 +01:00
SurfaceProperties surfProps;
MaterialList *ret;
2017-07-11 08:18:15 +02:00
static SurfaceProperties reset = { 1.0f, 1.0f, 1.0f };
2017-03-16 11:42:59 +01:00
if(!findChunk(stream, ID_STRUCT, nil, &version)){
RWERROR((ERR_CHUNK, "STRUCT"));
return nil;
}
stream->read(&buf, sizeof(buf));
Geometry *geo = Geometry::create(buf.numVertices,
buf.numTriangles, buf.flags);
if(geo == nil)
return nil;
2014-12-19 22:58:10 +01:00
geo->addMorphTargets(buf.numMorphTargets-1);
2014-12-18 17:26:57 +01:00
if(version < 0x34000)
2017-03-16 11:42:59 +01:00
stream->read(&surfProps, 12);
2014-12-18 17:26:57 +01:00
2017-03-16 11:42:59 +01:00
if(!(geo->flags & NATIVE)){
if(geo->flags & PRELIT)
stream->read(geo->colors, 4*geo->numVertices);
2014-12-25 09:06:42 +01:00
for(int32 i = 0; i < geo->numTexCoordSets; i++)
stream->read(geo->texCoords[i],
2014-12-25 09:06:42 +01:00
2*geo->numVertices*4);
2016-01-24 01:42:51 +01:00
for(int32 i = 0; i < geo->numTriangles; i++){
uint32 tribuf[2];
stream->read(tribuf, 8);
geo->triangles[i].v[0] = tribuf[0] >> 16;
geo->triangles[i].v[1] = tribuf[0];
geo->triangles[i].v[2] = tribuf[1] >> 16;
geo->triangles[i].matId = tribuf[1];
}
2014-12-18 17:26:57 +01:00
}
for(int32 i = 0; i < geo->numMorphTargets; i++){
MorphTarget *m = &geo->morphTargets[i];
stream->read(&m->boundingSphere, 4*4);
int32 hasVertices = stream->readI32();
int32 hasNormals = stream->readI32();
2014-12-19 22:58:10 +01:00
if(hasVertices)
stream->read(m->vertices, 3*geo->numVertices*4);
2014-12-19 22:58:10 +01:00
if(hasNormals)
stream->read(m->normals, 3*geo->numVertices*4);
2014-12-18 17:26:57 +01:00
}
if(!findChunk(stream, ID_MATLIST, nil, nil)){
RWERROR((ERR_CHUNK, "MATLIST"));
2016-06-17 13:29:49 +02:00
goto fail;
}
2017-03-16 11:42:59 +01:00
if(version < 0x34000)
defaultSurfaceProps = surfProps;
2017-08-25 14:06:53 +02:00
2017-03-16 11:42:59 +01:00
ret = MaterialList::streamRead(stream, &geo->matList);
if(version < 0x34000)
2017-07-11 08:18:15 +02:00
defaultSurfaceProps = reset;
2017-03-16 11:42:59 +01:00
if(ret == nil)
2016-06-17 13:29:49 +02:00
goto fail;
if(s_plglist.streamRead(stream, geo))
2016-06-17 13:29:49 +02:00
return geo;
2014-12-18 17:26:57 +01:00
2016-06-17 13:29:49 +02:00
fail:
geo->destroy();
return nil;
2014-12-18 17:26:57 +01:00
}
static uint32
geoStructSize(Geometry *geo)
{
uint32 size = 0;
size += sizeof(GeoStreamData);
if(version < 0x34000)
2014-12-18 17:26:57 +01:00
size += 12; // surface properties
2017-03-16 11:42:59 +01:00
if(!(geo->flags & Geometry::NATIVE)){
if(geo->flags&geo->PRELIT)
2014-12-18 17:26:57 +01:00
size += 4*geo->numVertices;
2014-12-25 09:06:42 +01:00
for(int32 i = 0; i < geo->numTexCoordSets; i++)
size += 2*geo->numVertices*4;
2014-12-18 17:26:57 +01:00
size += 4*geo->numTriangles*2;
}
for(int32 i = 0; i < geo->numMorphTargets; i++){
MorphTarget *m = &geo->morphTargets[i];
size += 4*4 + 2*4; // bounding sphere and bools
2017-03-16 11:42:59 +01:00
if(!(geo->flags & Geometry::NATIVE)){
if(m->vertices)
size += 3*geo->numVertices*4;
if(m->normals)
size += 3*geo->numVertices*4;
}
2014-12-18 17:26:57 +01:00
}
return size;
}
bool
Geometry::streamWrite(Stream *stream)
2014-12-18 17:26:57 +01:00
{
GeoStreamData buf;
static float32 fbuf[3] = { 1.0f, 1.0f, 1.0f };
writeChunkHeader(stream, ID_GEOMETRY, this->streamGetSize());
writeChunkHeader(stream, ID_STRUCT, geoStructSize(this));
2014-12-18 17:26:57 +01:00
2017-03-16 11:42:59 +01:00
buf.flags = this->flags | this->numTexCoordSets << 16;
2014-12-18 17:26:57 +01:00
buf.numTriangles = this->numTriangles;
buf.numVertices = this->numVertices;
buf.numMorphTargets = this->numMorphTargets;
stream->write(&buf, sizeof(buf));
if(version < 0x34000)
stream->write(fbuf, sizeof(fbuf));
2014-12-18 17:26:57 +01:00
2017-03-16 11:42:59 +01:00
if(!(this->flags & NATIVE)){
if(this->flags & PRELIT)
stream->write(this->colors, 4*this->numVertices);
2014-12-25 09:06:42 +01:00
for(int32 i = 0; i < this->numTexCoordSets; i++)
stream->write(this->texCoords[i],
2014-12-25 09:06:42 +01:00
2*this->numVertices*4);
2016-01-24 01:42:51 +01:00
for(int32 i = 0; i < this->numTriangles; i++){
uint32 tribuf[2];
tribuf[0] = this->triangles[i].v[0] << 16 |
this->triangles[i].v[1];
tribuf[1] = this->triangles[i].v[2] << 16 |
this->triangles[i].matId;
stream->write(tribuf, 8);
}
2014-12-18 17:26:57 +01:00
}
for(int32 i = 0; i < this->numMorphTargets; i++){
MorphTarget *m = &this->morphTargets[i];
stream->write(&m->boundingSphere, 4*4);
2017-03-16 11:42:59 +01:00
if(!(this->flags & NATIVE)){
stream->writeI32(m->vertices != nil);
stream->writeI32(m->normals != nil);
if(m->vertices)
stream->write(m->vertices,
3*this->numVertices*4);
if(m->normals)
stream->write(m->normals,
3*this->numVertices*4);
}else{
stream->writeI32(0);
stream->writeI32(0);
}
2014-12-18 17:26:57 +01:00
}
2017-03-16 11:42:59 +01:00
this->matList.streamWrite(stream);
2014-12-18 17:26:57 +01:00
s_plglist.streamWrite(stream, this);
2014-12-18 17:26:57 +01:00
return true;
}
uint32
Geometry::streamGetSize(void)
{
uint32 size = 0;
size += 12 + geoStructSize(this);
2017-03-16 11:42:59 +01:00
size += 12 + this->matList.streamGetSize();
size += 12 + s_plglist.streamGetSize(this);
2014-12-18 17:26:57 +01:00
return size;
}
2014-12-19 22:58:10 +01:00
void
Geometry::addMorphTargets(int32 n)
{
if(n == 0)
return;
2017-08-24 15:10:34 +02:00
n += this->numMorphTargets;
2017-08-25 14:06:53 +02:00
int32 sz;
sz = sizeof(MorphTarget);
if(!(this->flags & NATIVE)){
sz += this->numVertices*sizeof(V3d);
if(this->flags & NORMALS)
sz += this->numVertices*sizeof(V3d);
}
// Memory layout: MorphTarget[n]; (vertices and normals)[n]
MorphTarget *mts;
if(this->numMorphTargets){
mts = (MorphTarget*)rwResize(this->morphTargets, n*sz, MEMDUR_EVENT | ID_GEOMETRY);
this->morphTargets = mts;
// Since we now have more morph targets than before, move the vertex data up
uint8 *src = (uint8*)mts + sz*this->numMorphTargets;
uint8 *dst = (uint8*)mts + sz*n;
uint32 len = (sz-sizeof(MorphTarget))*this->numMorphTargets;
while(len--)
*--dst = *--src;
}else{
mts = (MorphTarget*)rwNew(n*sz, MEMDUR_EVENT | ID_GEOMETRY);
this->morphTargets = mts;
}
// Set up everything and initialize the bounding sphere for new morph targets
V3d *data = (V3d*)&mts[n];
for(int32 i = 0; i < n; i++){
mts->parent = this;
mts->vertices = nil;
mts->normals = nil;
if(i >= this->numMorphTargets){
mts->boundingSphere.center.x = 0.0f;
mts->boundingSphere.center.y = 0.0f;
mts->boundingSphere.center.z = 0.0f;
mts->boundingSphere.radius = 0.0f;
}
if(!(this->flags & NATIVE) && this->numVertices){
mts->vertices = data;
data += this->numVertices;
if(this->flags & NORMALS){
mts->normals = data;
data += this->numVertices;
}
2014-12-19 22:58:10 +01:00
}
2017-08-25 14:06:53 +02:00
mts++;
2014-12-19 22:58:10 +01:00
}
2017-08-24 15:10:34 +02:00
this->numMorphTargets = n;
2014-12-19 22:58:10 +01:00
}
2014-12-18 17:26:57 +01:00
2015-12-19 17:05:39 +01:00
void
Geometry::calculateBoundingSphere(void)
{
for(int32 i = 0; i < this->numMorphTargets; i++){
MorphTarget *m = &this->morphTargets[i];
2017-08-10 14:47:28 +02:00
V3d min = { 1000000.0f, 1000000.0f, 1000000.0f };
V3d max = { -1000000.0f, -1000000.0f, -1000000.0f };
2017-08-05 01:44:37 +02:00
V3d *v = m->vertices;
2015-12-19 17:05:39 +01:00
for(int32 j = 0; j < this->numVertices; j++){
2017-08-05 01:44:37 +02:00
if(v->x > max.x) max.x = v->x;
if(v->x < min.x) min.x = v->x;
if(v->y > max.y) max.y = v->y;
if(v->y < min.y) min.y = v->y;
if(v->z > max.z) max.z = v->z;
if(v->z < min.z) min.z = v->z;
v++;
2015-12-19 17:05:39 +01:00
}
m->boundingSphere.center = scale(add(min, max), 1/2.0f);
max = sub(max, m->boundingSphere.center);
m->boundingSphere.radius = length(max);
2015-12-19 17:05:39 +01:00
}
}
bool32
Geometry::hasColoredMaterial(void)
{
2017-03-16 11:42:59 +01:00
for(int32 i = 0; i < this->matList.numMaterials; i++)
if(this->matList.materials[i]->color.red != 255 ||
this->matList.materials[i]->color.green != 255 ||
this->matList.materials[i]->color.blue != 255 ||
this->matList.materials[i]->color.alpha != 255)
2015-12-19 17:05:39 +01:00
return 1;
return 0;
}
2017-08-25 14:06:53 +02:00
// Force allocate data, even when native flag is set
2015-08-11 20:57:43 +02:00
void
Geometry::allocateData(void)
{
2017-08-25 14:06:53 +02:00
// Geometry data
// Pretty much copy pasted from ::create above
int32 sz = this->numTriangles*sizeof(Triangle);
2017-03-16 11:42:59 +01:00
if(this->flags & PRELIT)
2017-08-25 14:06:53 +02:00
sz += this->numVertices*sizeof(RGBA);
sz += this->numTexCoordSets*this->numVertices*sizeof(TexCoords);
uint8 *data = (uint8*)rwNew(sz, MEMDUR_EVENT | ID_GEOMETRY);
this->triangles = (Triangle*)data;
data += this->numTriangles*sizeof(Triangle);
for(int32 i = 0; i < this->numTriangles; i++)
this->triangles[i].matId = 0xFFFF;
if(this->flags & PRELIT){
this->colors = (RGBA*)data;
data += this->numVertices*sizeof(RGBA);
}
for(int32 i = 0; i < this->numTexCoordSets; i++){
this->texCoords[i] = (TexCoords*)data;
data += this->numVertices*sizeof(TexCoords);
}
// MorphTarget data
// Bounding sphere is copied by realloc.
sz = sizeof(MorphTarget) + this->numVertices*sizeof(V3d);
if(this->flags & NORMALS)
sz += this->numVertices*sizeof(V3d);
MorphTarget *mt = (MorphTarget*)rwResize(this->morphTargets,
sz*this->numMorphTargets, MEMDUR_EVENT | ID_GEOMETRY);
this->morphTargets = mt;
V3d *vdata = (V3d*)&mt[this->numMorphTargets];
2017-08-24 15:10:34 +02:00
for(int32 i = 0; i < this->numMorphTargets; i++){
2017-08-25 14:06:53 +02:00
mt->parent = this;
mt->vertices = nil;
mt->normals = nil;
if(this->numVertices){
mt->vertices = vdata;
vdata += this->numVertices;
if(this->flags & NORMALS){
mt->normals = vdata;
vdata += this->numVertices;
}
}
mt++;
2017-08-24 15:10:34 +02:00
}
2015-08-11 20:57:43 +02:00
}
static int
isDegenerate(uint16 *idx)
{
return idx[0] == idx[1] ||
idx[0] == idx[2] ||
idx[1] == idx[2];
}
2017-08-25 14:06:53 +02:00
// This functions assumes there is enough space allocated
// for triangles. Use MeshHeader::guessNumTriangles() and
// Geometry::allocateData()
2015-08-11 20:57:43 +02:00
void
Geometry::generateTriangles(int8 *adc)
2015-08-11 20:57:43 +02:00
{
MeshHeader *header = this->meshHeader;
assert(header != nil);
2015-08-11 20:57:43 +02:00
this->numTriangles = 0;
2017-08-25 14:06:53 +02:00
Mesh *m = header->getMeshes();
int8 *adcbits = adc;
2015-08-11 20:57:43 +02:00
for(uint32 i = 0; i < header->numMeshes; i++){
2015-09-19 19:28:23 +02:00
if(m->numIndices < 3){
// shouldn't happen but it does
adcbits += m->numIndices;
2015-09-19 19:28:23 +02:00
m++;
continue;
}
if(header->flags == MeshHeader::TRISTRIP){
2015-08-11 20:57:43 +02:00
for(uint32 j = 0; j < m->numIndices-2; j++){
if(!(adc && adcbits[j+2]) &&
!isDegenerate(&m->indices[j]))
2015-08-11 20:57:43 +02:00
this->numTriangles++;
}
}else
this->numTriangles += m->numIndices/3;
adcbits += m->numIndices;
2015-08-11 20:57:43 +02:00
m++;
}
2016-01-24 01:42:51 +01:00
Triangle *tri = this->triangles;
2017-08-25 14:06:53 +02:00
m = header->getMeshes();
adcbits = adc;
2015-08-11 20:57:43 +02:00
for(uint32 i = 0; i < header->numMeshes; i++){
2015-09-19 19:28:23 +02:00
if(m->numIndices < 3){
adcbits += m->numIndices;
2015-09-19 19:28:23 +02:00
m++;
continue;
}
2017-03-16 11:42:59 +01:00
int32 matid = this->matList.findIndex(m->material);
if(header->flags == MeshHeader::TRISTRIP)
2015-08-11 20:57:43 +02:00
for(uint32 j = 0; j < m->numIndices-2; j++){
if(adc && adcbits[j+2] ||
isDegenerate(&m->indices[j]))
2015-08-11 20:57:43 +02:00
continue;
2016-01-24 01:42:51 +01:00
tri->v[0] = m->indices[j+0];
tri->v[1] = m->indices[j+1 + (j%2)];
tri->v[2] = m->indices[j+2 - (j%2)];
tri->matId = matid;
tri++;
2015-08-11 20:57:43 +02:00
}
else
for(uint32 j = 0; j < m->numIndices-2; j+=3){
2016-01-24 01:42:51 +01:00
tri->v[0] = m->indices[j+0];
tri->v[1] = m->indices[j+1];
tri->v[2] = m->indices[j+2];
tri->matId = matid;
tri++;
2015-08-11 20:57:43 +02:00
}
adcbits += m->numIndices;
2015-08-11 20:57:43 +02:00
m++;
}
}
2014-12-18 17:26:57 +01:00
static void
dumpMesh(Mesh *m)
{
for(int32 i = 0; i < m->numIndices-2; i++)
// if(i % 2)
// printf("%3d %3d %3d\n",
// m->indices[i+1],
// m->indices[i],
// m->indices[i+2]);
// else
printf("%d %d %d\n",
m->indices[i],
m->indices[i+1],
m->indices[i+2]);
}
2016-01-24 01:42:51 +01:00
void
Geometry::buildMeshes(void)
{
Triangle *tri;
2017-08-25 14:06:53 +02:00
Mesh *mesh;
rwFree(this->meshHeader);
this->meshHeader = nil;
int32 numMeshes = this->matList.numMaterials;
2017-03-16 11:42:59 +01:00
if((this->flags & Geometry::TRISTRIP) == 0){
2017-08-25 14:06:53 +02:00
int32 *numIndices = rwNewT(int32, numMeshes,
MEMDUR_FUNCTION | ID_GEOMETRY);
memset(numIndices, 0, numMeshes*sizeof(int32));
2016-01-24 01:42:51 +01:00
// count indices per mesh
tri = this->triangles;
for(int32 i = 0; i < this->numTriangles; i++){
2017-08-29 14:05:45 +02:00
assert(tri->matId < numMeshes);
2017-08-25 14:06:53 +02:00
numIndices[tri->matId] += 3;
2016-01-24 01:42:51 +01:00
tri++;
}
2017-08-25 14:06:53 +02:00
// setup meshes
this->allocateMeshes(numMeshes, this->numTriangles*3, 0);
mesh = this->meshHeader->getMeshes();
for(int32 i = 0; i < numMeshes; i++){
mesh[i].material = this->matList.materials[i];
mesh[i].numIndices = numIndices[i];
}
this->meshHeader->setupIndices();
rwFree(numIndices);
// now fill in the indices
for(int32 i = 0; i < numMeshes; i++)
mesh[i].numIndices = 0;
2016-01-24 01:42:51 +01:00
tri = this->triangles;
for(int32 i = 0; i < this->numTriangles; i++){
2017-08-25 14:06:53 +02:00
uint32 idx = mesh[tri->matId].numIndices;
mesh[tri->matId].indices[idx++] = tri->v[0];
mesh[tri->matId].indices[idx++] = tri->v[1];
mesh[tri->matId].indices[idx++] = tri->v[2];
mesh[tri->matId].numIndices = idx;
2016-01-24 01:42:51 +01:00
tri++;
}
}else
this->buildTristrips();
2016-01-24 01:42:51 +01:00
}
/* The idea is that even in meshes where winding is not preserved
* every tristrip starts at an even vertex. So find the start of
* strips and insert duplicate vertices if necessary. */
void
Geometry::correctTristripWinding(void)
{
MeshHeader *header = this->meshHeader;
2017-08-25 14:06:53 +02:00
if(this->flags & NATIVE || header == nil ||
header->flags != MeshHeader::TRISTRIP)
return;
2017-08-25 14:06:53 +02:00
this->meshHeader = nil;
// Allocate no indices, we realloc later
MeshHeader *newhead = this->allocateMeshes(header->numMeshes, 0, 1);
newhead->flags = header->flags;
/* get a temporary working buffer */
2017-08-25 14:06:53 +02:00
uint16 *indices = rwNewT(uint16, header->totalIndices*2,
MEMDUR_FUNCTION | ID_GEOMETRY);
2017-08-25 14:06:53 +02:00
Mesh *mesh = header->getMeshes();
Mesh *newmesh = newhead->getMeshes();
for(uint16 i = 0; i < header->numMeshes; i++){
newmesh->numIndices = 0;
newmesh->indices = &indices[newhead->totalIndices];
newmesh->material = mesh->material;
bool inStrip = 0;
uint32 j;
for(j = 0; j < mesh->numIndices-2; j++){
/* Duplicate vertices indicate end of strip */
if(mesh->indices[j] == mesh->indices[j+1] ||
mesh->indices[j+1] == mesh->indices[j+2])
inStrip = 0;
else if(!inStrip){
/* Entering strip now,
* make sure winding is correct */
inStrip = 1;
if(newmesh->numIndices % 2){
newmesh->indices[newmesh->numIndices] =
newmesh->indices[newmesh->numIndices-1];
newmesh->numIndices++;
}
}
newmesh->indices[newmesh->numIndices++] = mesh->indices[j];
}
for(; j < mesh->numIndices; j++)
newmesh->indices[newmesh->numIndices++] = mesh->indices[j];
newhead->totalIndices += newmesh->numIndices;
mesh++;
newmesh++;
}
2017-08-25 14:06:53 +02:00
rwFree(header);
// Now allocate indices and copy them
this->allocateMeshes(newhead->numMeshes, newhead->totalIndices, 0);
memcpy(this->meshHeader->getMeshes()->indices, indices, newhead->totalIndices*2);
rwFree(indices);
}
void
Geometry::removeUnusedMaterials(void)
{
if(this->meshHeader == nil)
return;
MeshHeader *mh = this->meshHeader;
2017-08-25 14:06:53 +02:00
Mesh *m = mh->getMeshes();
for(uint32 i = 0; i < mh->numMeshes; i++)
2017-08-25 14:06:53 +02:00
if(m[i].indices == nil)
return;
2017-03-16 11:42:59 +01:00
2017-08-25 14:06:53 +02:00
int32 *map = rwNewT(int32, this->matList.numMaterials,
MEMDUR_FUNCTION | ID_GEOMETRY);
Material **materials = rwNewT(Material*,this->matList.numMaterials,
MEMDUR_EVENT | ID_MATERIAL);
int32 numMaterials = 0;
/* Build new material list and map */
for(uint32 i = 0; i < mh->numMeshes; i++){
if(m->numIndices <= 0)
continue;
2017-03-16 11:42:59 +01:00
materials[numMaterials] = m->material;
m->material->refCount++;
int32 oldid = this->matList.findIndex(m->material);
map[oldid] = numMaterials;
numMaterials++;
2017-08-25 14:06:53 +02:00
m++;
}
2017-03-16 11:42:59 +01:00
for(int32 i = 0; i < this->matList.numMaterials; i++)
this->matList.materials[i]->destroy();
2017-08-24 15:10:34 +02:00
rwFree(this->matList.materials);
2017-03-16 11:42:59 +01:00
this->matList.materials = materials;
this->matList.space = this->matList.numMaterials;
this->matList.numMaterials = numMaterials;
/* Build new meshes */
2017-08-25 14:06:53 +02:00
this->meshHeader = nil;
MeshHeader *newmh = this->allocateMeshes(numMaterials, mh->totalIndices, 0);
newmh->flags = mh->flags;
2017-08-25 14:06:53 +02:00
Mesh *newm = newmh->getMeshes();
m = mh->getMeshes();
for(uint32 i = 0; i < mh->numMeshes; i++){
2017-08-25 14:06:53 +02:00
if(m[i].numIndices <= 0)
continue;
2017-08-25 14:06:53 +02:00
newm->numIndices = m[i].numIndices;
newm->material = m[i].material;
newm++;
}
2017-08-25 14:06:53 +02:00
newmh->setupIndices();
/* Copy indices */
2017-08-25 14:06:53 +02:00
newm = newmh->getMeshes();;
m = mh->getMeshes();
for(uint32 i = 0; i < mh->numMeshes; i++){
2017-08-25 14:06:53 +02:00
if(m[i].numIndices <= 0)
continue;
2017-08-25 14:06:53 +02:00
memcpy(newm->indices, m[i].indices,
m[i].numIndices*sizeof(*m[i].indices));
newm++;
}
2017-08-25 14:06:53 +02:00
rwFree(mh);
2017-03-16 11:42:59 +01:00
/* Remap triangle material IDs */
for(int32 i = 0; i < this->numTriangles; i++)
this->triangles[i].matId = map[this->triangles[i].matId];
2017-08-25 14:06:53 +02:00
rwFree(map);
}
2017-03-16 11:42:59 +01:00
//
// MaterialList
//
2017-08-24 15:10:34 +02:00
#undef PLUGIN_ID
#define PLUGIN_ID ID_MATERIAL
2017-03-16 11:42:59 +01:00
void
MaterialList::init(void)
{
this->materials = nil;
this->numMaterials = 0;
this->space = 0;
}
void
MaterialList::deinit(void)
{
if(this->materials){
for(int32 i = 0; i < this->numMaterials; i++)
this->materials[i]->destroy();
2017-08-24 15:10:34 +02:00
rwFree(this->materials);
2017-03-16 11:42:59 +01:00
}
}
int32
MaterialList::appendMaterial(Material *mat)
{
Material **ml;
int32 space;
if(this->numMaterials >= this->space){
space = this->space + 20;
if(this->materials)
2017-08-25 14:06:53 +02:00
ml = rwReallocT(Material*, this->materials, space,
2017-08-24 15:10:34 +02:00
MEMDUR_EVENT | ID_MATERIAL);
2017-03-16 11:42:59 +01:00
else
2017-08-25 14:06:53 +02:00
ml = rwMallocT(Material*, space, MEMDUR_EVENT | ID_MATERIAL);
2017-03-16 11:42:59 +01:00
if(ml == nil)
return -1;
this->space = space;
this->materials = ml;
}
this->materials[this->numMaterials++] = mat;
mat->refCount++;
return this->numMaterials-1;
}
int32
MaterialList::findIndex(Material *mat)
{
for(int32 i = 0; i < this->numMaterials; i++)
if(this->materials[i] == mat)
return i;
return -1;
}
MaterialList*
MaterialList::streamRead(Stream *stream, MaterialList *matlist)
{
int32 *indices = nil;
int32 numMat;
if(!findChunk(stream, ID_STRUCT, nil, nil)){
RWERROR((ERR_CHUNK, "STRUCT"));
goto fail;
}
matlist->init();
numMat = stream->readI32();
if(numMat == 0)
return matlist;
2017-08-25 14:06:53 +02:00
matlist->materials = rwMallocT(Material*,numMat, MEMDUR_EVENT | ID_MATERIAL);
2017-03-16 11:42:59 +01:00
if(matlist->materials == nil)
goto fail;
matlist->space = numMat;
2017-08-24 15:10:34 +02:00
indices = (int32*)rwMalloc(numMat*4, MEMDUR_FUNCTION | ID_MATERIAL);
2017-03-16 11:42:59 +01:00
stream->read(indices, numMat*4);
Material *m;
for(int32 i = 0; i < numMat; i++){
if(indices[i] >= 0){
m = matlist->materials[indices[i]];
m->refCount++;
}else{
if(!findChunk(stream, ID_MATERIAL, nil, nil)){
RWERROR((ERR_CHUNK, "MATERIAL"));
goto fail;
}
m = Material::streamRead(stream);
if(m == nil)
goto fail;
}
matlist->appendMaterial(m);
m->destroy();
}
2017-08-24 15:10:34 +02:00
rwFree(indices);
2017-03-16 11:42:59 +01:00
return matlist;
fail:
2017-08-24 15:10:34 +02:00
rwFree(indices);
2017-03-16 11:42:59 +01:00
matlist->deinit();
return nil;
}
bool
MaterialList::streamWrite(Stream *stream)
{
uint32 size = this->streamGetSize();
writeChunkHeader(stream, ID_MATLIST, size);
writeChunkHeader(stream, ID_STRUCT, 4 + this->numMaterials*4);
stream->writeI32(this->numMaterials);
int32 idx;
for(int32 i = 0; i < this->numMaterials; i++){
idx = -1;
for(int32 j = i-1; j >= 0; j--)
if(this->materials[i] == this->materials[j]){
idx = j;
break;
}
stream->writeI32(idx);
}
for(int32 i = 0; i < this->numMaterials; i++){
for(int32 j = i-1; j >= 0; j--)
if(this->materials[i] == this->materials[j])
goto found;
this->materials[i]->streamWrite(stream);
found:;
}
return true;
}
uint32
MaterialList::streamGetSize(void)
{
uint32 size = 12 + 4 + this->numMaterials*4;
for(int32 i = 0; i < this->numMaterials; i++){
for(int32 j = i-1; j >= 0; j--)
if(this->materials[i] == this->materials[j])
goto found;
size += 12 + this->materials[i]->streamGetSize();
found:;
}
return size;
}
2015-01-09 20:17:32 +01:00
//
// Material
//
2014-12-18 17:26:57 +01:00
Material*
Material::create(void)
2014-12-18 17:26:57 +01:00
{
2017-08-24 15:10:34 +02:00
Material *mat = (Material*)rwMalloc(s_plglist.size, MEMDUR_EVENT | ID_MATERIAL);
if(mat == nil){
RWERROR((ERR_ALLOC, s_plglist.size));
return nil;
}
mat->texture = nil;
2016-01-13 08:58:15 +01:00
memset(&mat->color, 0xFF, 4);
2017-03-16 11:42:59 +01:00
mat->surfaceProps = defaultSurfaceProps;
mat->pipeline = nil;
mat->refCount = 1;
s_plglist.construct(mat);
return mat;
2014-12-18 17:26:57 +01:00
}
Material*
Material::clone(void)
2014-12-18 17:26:57 +01:00
{
Material *mat = Material::create();
if(mat == nil){
RWERROR((ERR_ALLOC, s_plglist.size));
return nil;
}
2016-01-13 08:58:15 +01:00
mat->color = this->color;
mat->surfaceProps = this->surfaceProps;
2016-06-17 13:29:49 +02:00
if(this->texture)
mat->setTexture(this->texture);
mat->pipeline = this->pipeline;
s_plglist.copy(mat, this);
return mat;
2014-12-20 11:38:27 +01:00
}
void
Material::destroy(void)
2014-12-20 11:38:27 +01:00
{
this->refCount--;
2016-01-13 08:58:15 +01:00
if(this->refCount <= 0){
s_plglist.destruct(this);
if(this->texture)
this->texture->destroy();
2017-08-24 15:10:34 +02:00
rwFree(this);
}
2014-12-18 17:26:57 +01:00
}
2016-06-17 13:29:49 +02:00
void
Material::setTexture(Texture *tex)
{
if(this->texture)
this->texture->destroy();
if(tex)
tex->refCount++;
this->texture = tex;
}
2014-12-18 17:26:57 +01:00
struct MatStreamData
{
int32 flags; // unused according to RW
2016-01-13 08:58:15 +01:00
RGBA color;
2014-12-18 17:26:57 +01:00
int32 unused;
int32 textured;
};
2015-01-10 22:13:27 +01:00
static uint32 materialRights[2];
2014-12-18 17:26:57 +01:00
Material*
Material::streamRead(Stream *stream)
2014-12-18 17:26:57 +01:00
{
2015-08-16 23:23:41 +02:00
uint32 length, version;
2014-12-18 17:26:57 +01:00
MatStreamData buf;
2015-12-18 17:34:12 +01:00
if(!findChunk(stream, ID_STRUCT, nil, &version)){
RWERROR((ERR_CHUNK, "STRUCT"));
return nil;
}
stream->read(&buf, sizeof(buf));
Material *mat = Material::create();
if(mat == nil)
return nil;
2016-01-13 08:58:15 +01:00
mat->color = buf.color;
2017-03-16 11:42:59 +01:00
if(version < 0x30400)
mat->surfaceProps = defaultSurfaceProps;
else
2017-07-12 10:10:57 +02:00
stream->read(&mat->surfaceProps, sizeof(SurfaceProperties));
2014-12-18 17:26:57 +01:00
if(buf.textured){
if(!findChunk(stream, ID_TEXTURE, &length, nil)){
RWERROR((ERR_CHUNK, "TEXTURE"));
2016-06-17 13:29:49 +02:00
goto fail;
}
2016-06-17 13:29:49 +02:00
Texture *t = Texture::streamRead(stream);
mat->setTexture(t);
2014-12-18 17:26:57 +01:00
}
2015-01-10 22:13:27 +01:00
materialRights[0] = 0;
if(!s_plglist.streamRead(stream, mat))
2016-06-17 13:29:49 +02:00
goto fail;
2015-01-10 22:13:27 +01:00
if(materialRights[0])
s_plglist.assertRights(mat, materialRights[0], materialRights[1]);
2014-12-18 17:26:57 +01:00
return mat;
2016-06-17 13:29:49 +02:00
fail:
mat->destroy();
return nil;
2014-12-18 17:26:57 +01:00
}
bool
Material::streamWrite(Stream *stream)
2014-12-18 17:26:57 +01:00
{
MatStreamData buf;
writeChunkHeader(stream, ID_MATERIAL, this->streamGetSize());
2015-08-16 23:23:41 +02:00
writeChunkHeader(stream, ID_STRUCT, sizeof(MatStreamData)
+ (rw::version >= 0x30400 ? 12 : 0));
2014-12-18 17:26:57 +01:00
2016-01-13 08:58:15 +01:00
buf.color = this->color;
2014-12-18 17:26:57 +01:00
buf.flags = 0;
buf.unused = 0;
buf.textured = this->texture != nil;
stream->write(&buf, sizeof(buf));
2014-12-18 17:26:57 +01:00
2015-08-16 23:23:41 +02:00
if(rw::version >= 0x30400){
float32 surfaceProps[3];
2016-01-09 22:01:21 +01:00
surfaceProps[0] = this->surfaceProps.ambient;
surfaceProps[1] = this->surfaceProps.specular;
surfaceProps[2] = this->surfaceProps.diffuse;
2015-08-16 23:23:41 +02:00
stream->write(surfaceProps, sizeof(surfaceProps));
}
2014-12-18 17:26:57 +01:00
if(this->texture)
this->texture->streamWrite(stream);
s_plglist.streamWrite(stream, this);
2014-12-18 17:26:57 +01:00
return true;
}
uint32
Material::streamGetSize(void)
{
uint32 size = 0;
size += 12 + sizeof(MatStreamData);
2015-08-16 23:23:41 +02:00
if(rw::version >= 0x30400)
size += 12;
2014-12-18 17:26:57 +01:00
if(this->texture)
size += 12 + this->texture->streamGetSize();
size += 12 + s_plglist.streamGetSize(this);
2014-12-18 17:26:57 +01:00
return size;
}
2015-01-09 20:17:32 +01:00
// Material Rights plugin
2016-06-17 16:20:02 +02:00
static Stream*
2015-01-09 20:17:32 +01:00
readMaterialRights(Stream *stream, int32, void *, int32, int32)
{
2015-01-10 22:13:27 +01:00
stream->read(materialRights, 8);
2015-08-01 23:03:10 +02:00
// printf("materialrights: %X %X\n", materialRights[0], materialRights[1]);
2016-06-17 16:20:02 +02:00
return stream;
2015-01-10 22:13:27 +01:00
}
2016-06-17 16:20:02 +02:00
static Stream*
2015-01-10 22:13:27 +01:00
writeMaterialRights(Stream *stream, int32, void *object, int32, int32)
{
Material *material = (Material*)object;
2015-01-09 20:17:32 +01:00
uint32 buffer[2];
2015-01-10 22:13:27 +01:00
buffer[0] = material->pipeline->pluginID;
buffer[1] = material->pipeline->pluginData;
stream->write(buffer, 8);
2016-06-17 16:20:02 +02:00
return stream;
2015-01-10 22:13:27 +01:00
}
static int32
getSizeMaterialRights(void *object, int32, int32)
{
Material *material = (Material*)object;
if(material->pipeline == nil || material->pipeline->pluginID == 0)
2016-06-17 16:20:02 +02:00
return 0;
2015-01-10 22:13:27 +01:00
return 8;
2015-01-09 20:17:32 +01:00
}
void
registerMaterialRightsPlugin(void)
2015-01-09 20:17:32 +01:00
{
Material::registerPlugin(0, ID_RIGHTTORENDER, nil, nil, nil);
2015-01-09 20:17:32 +01:00
Material::registerPluginStream(ID_RIGHTTORENDER,
2015-01-10 22:13:27 +01:00
readMaterialRights,
writeMaterialRights,
getSizeMaterialRights);
2015-01-09 20:17:32 +01:00
}
2014-12-18 17:26:57 +01:00
}