#include #include #include #include #include #include "rwbase.h" #include "rwerror.h" #include "rwplg.h" #include "rwpipeline.h" #include "rwobjects.h" #define PLUGIN_ID 2 namespace rw { Geometry* Geometry::create(int32 numVerts, int32 numTris, uint32 flags) { Geometry *geo = (Geometry*)malloc(s_plglist.size); if(geo == nil){ RWERROR((ERR_ALLOC, s_plglist.size)); return nil; } geo->object.init(Geometry::ID, 0); geo->geoflags = flags & 0xFF00FFFF; geo->numTexCoordSets = (flags & 0xFF0000) >> 16; if(geo->numTexCoordSets == 0) geo->numTexCoordSets = (geo->geoflags & TEXTURED) ? 1 : (geo->geoflags & TEXTURED2) ? 2 : 0; geo->numTriangles = numTris; geo->numVertices = numVerts; geo->numMorphTargets = 1; geo->colors = nil; for(int32 i = 0; i < geo->numTexCoordSets; i++) geo->texCoords[i] = nil; geo->triangles = nil; if(!(geo->geoflags & NATIVE) && geo->numVertices){ if(geo->geoflags & PRELIT) geo->colors = new uint8[4*geo->numVertices]; if((geo->geoflags & TEXTURED) || (geo->geoflags & TEXTURED2)) for(int32 i = 0; i < geo->numTexCoordSets; i++) geo->texCoords[i] = new float32[2*geo->numVertices]; geo->triangles = new Triangle[geo->numTriangles]; } geo->morphTargets = new MorphTarget[1]; MorphTarget *m = geo->morphTargets; m->boundingSphere.center.set(0.0f, 0.0f, 0.0f); m->boundingSphere.radius = 0.0f; m->vertices = nil; m->normals = nil; if(!(geo->geoflags & NATIVE) && geo->numVertices){ m->vertices = new float32[3*geo->numVertices]; if(geo->geoflags & NORMALS) m->normals = new float32[3*geo->numVertices]; } geo->numMaterials = 0; geo->materialList = nil; geo->meshHeader = nil; geo->instData = nil; geo->refCount = 1; s_plglist.construct(geo); return geo; } void Geometry::destroy(void) { this->refCount--; if(this->refCount <= 0){ s_plglist.destruct(this); delete[] this->colors; for(int32 i = 0; i < this->numTexCoordSets; i++) delete[] this->texCoords[i]; delete[] this->triangles; for(int32 i = 0; i < this->numMorphTargets; i++){ MorphTarget *m = &this->morphTargets[i]; delete[] m->vertices; delete[] m->normals; } delete[] this->morphTargets; delete this->meshHeader; for(int32 i = 0; i < this->numMaterials; i++) if(this->materialList[i]) this->materialList[i]->destroy(); delete[] this->materialList; free(this); } } struct GeoStreamData { uint32 flags; int32 numTriangles; int32 numVertices; int32 numMorphTargets; }; Geometry* Geometry::streamRead(Stream *stream) { uint32 version; GeoStreamData buf; 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; geo->addMorphTargets(buf.numMorphTargets-1); // skip surface properties if(version < 0x34000) stream->seek(12); if(!(geo->geoflags & NATIVE)){ if(geo->geoflags & PRELIT) stream->read(geo->colors, 4*geo->numVertices); for(int32 i = 0; i < geo->numTexCoordSets; i++) stream->read(geo->texCoords[i], 2*geo->numVertices*4); 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]; } } 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(); if(hasVertices) stream->read(m->vertices, 3*geo->numVertices*4); if(hasNormals) stream->read(m->normals, 3*geo->numVertices*4); } if(!findChunk(stream, ID_MATLIST, nil, nil)){ RWERROR((ERR_CHUNK, "MATLIST")); goto fail; } if(!findChunk(stream, ID_STRUCT, nil, nil)){ RWERROR((ERR_CHUNK, "STRUCT")); goto fail; } geo->numMaterials = stream->readI32(); geo->materialList = new Material*[geo->numMaterials]; stream->seek(geo->numMaterials*4); // material indices...but always -1 Material *m; for(int32 i = 0; i < geo->numMaterials; i++){ if(!findChunk(stream, ID_MATERIAL, nil, nil)){ RWERROR((ERR_CHUNK, "MATERIAL")); goto fail; } m = Material::streamRead(stream); if(m == nil) goto fail; geo->materialList[i] = m; } if(s_plglist.streamRead(stream, geo)) return geo; fail: geo->destroy(); return nil; } static uint32 geoStructSize(Geometry *geo) { uint32 size = 0; size += sizeof(GeoStreamData); if(version < 0x34000) size += 12; // surface properties if(!(geo->geoflags & Geometry::NATIVE)){ if(geo->geoflags&geo->PRELIT) size += 4*geo->numVertices; for(int32 i = 0; i < geo->numTexCoordSets; i++) size += 2*geo->numVertices*4; 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 if(!(geo->geoflags & Geometry::NATIVE)){ if(m->vertices) size += 3*geo->numVertices*4; if(m->normals) size += 3*geo->numVertices*4; } } return size; } bool Geometry::streamWrite(Stream *stream) { GeoStreamData buf; uint32 size; static float32 fbuf[3] = { 1.0f, 1.0f, 1.0f }; writeChunkHeader(stream, ID_GEOMETRY, this->streamGetSize()); writeChunkHeader(stream, ID_STRUCT, geoStructSize(this)); buf.flags = this->geoflags | this->numTexCoordSets << 16; 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)); if(!(this->geoflags & NATIVE)){ if(this->geoflags & PRELIT) stream->write(this->colors, 4*this->numVertices); for(int32 i = 0; i < this->numTexCoordSets; i++) stream->write(this->texCoords[i], 2*this->numVertices*4); 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); } } for(int32 i = 0; i < this->numMorphTargets; i++){ MorphTarget *m = &this->morphTargets[i]; stream->write(&m->boundingSphere, 4*4); if(!(this->geoflags & 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); } } size = 12 + 4; for(int32 i = 0; i < this->numMaterials; i++) size += 4 + 12 + this->materialList[i]->streamGetSize(); writeChunkHeader(stream, ID_MATLIST, size); writeChunkHeader(stream, ID_STRUCT, 4 + this->numMaterials*4); stream->writeI32(this->numMaterials); for(int32 i = 0; i < this->numMaterials; i++) stream->writeI32(-1); for(int32 i = 0; i < this->numMaterials; i++) this->materialList[i]->streamWrite(stream); s_plglist.streamWrite(stream, this); return true; } uint32 Geometry::streamGetSize(void) { uint32 size = 0; size += 12 + geoStructSize(this); size += 12 + 12 + 4; for(int32 i = 0; i < this->numMaterials; i++) size += 4 + 12 + this->materialList[i]->streamGetSize(); size += 12 + s_plglist.streamGetSize(this); return size; } void Geometry::addMorphTargets(int32 n) { if(n == 0) return; MorphTarget *morphTargets = new MorphTarget[this->numMorphTargets+n]; memcpy(morphTargets, this->morphTargets, this->numMorphTargets*sizeof(MorphTarget)); delete[] this->morphTargets; this->morphTargets = morphTargets; for(int32 i = this->numMorphTargets; i < n; i++){ MorphTarget *m = &morphTargets[i]; m->vertices = nil; m->normals = nil; if(!(this->geoflags & NATIVE)){ m->vertices = new float32[3*this->numVertices]; if(this->geoflags & NORMALS) m->normals = new float32[3*this->numVertices]; } } this->numMorphTargets += n; } void Geometry::calculateBoundingSphere(void) { for(int32 i = 0; i < this->numMorphTargets; i++){ MorphTarget *m = &this->morphTargets[i]; V3d min( 1000000.0f, 1000000.0f, 1000000.0f); V3d max(-1000000.0f, -1000000.0f, -1000000.0f); float32 *v = m->vertices; for(int32 j = 0; j < this->numVertices; j++){ if(v[0] > max.x) max.x = v[0]; if(v[0] < min.x) min.x = v[0]; if(v[1] > max.y) max.y = v[1]; if(v[1] < min.y) min.y = v[1]; if(v[2] > max.z) max.z = v[2]; if(v[2] < min.z) min.z = v[2]; v += 3; } m->boundingSphere.center = scale(add(min, max), 1/2.0f); max = sub(max, m->boundingSphere.center); m->boundingSphere.radius = length(max); } } bool32 Geometry::hasColoredMaterial(void) { for(int32 i = 0; i < this->numMaterials; i++) if(this->materialList[i]->color.red != 255 || this->materialList[i]->color.green != 255 || this->materialList[i]->color.blue != 255 || this->materialList[i]->color.alpha != 255) return 1; return 0; } void Geometry::allocateData(void) { if(this->geoflags & PRELIT) this->colors = new uint8[4*this->numVertices]; if((this->geoflags & TEXTURED) || (this->geoflags & TEXTURED2)) for(int32 i = 0; i < this->numTexCoordSets; i++) this->texCoords[i] = new float32[2*this->numVertices]; MorphTarget *m = this->morphTargets; m->vertices = new float32[3*this->numVertices]; if(this->geoflags & NORMALS) m->normals = new float32[3*this->numVertices]; // TODO: morph targets (who cares anyway?) } static int isDegenerate(uint16 *idx) { return idx[0] == idx[1] || idx[0] == idx[2] || idx[1] == idx[2]; } void Geometry::generateTriangles(int8 *adc) { MeshHeader *header = this->meshHeader; assert(header != nil); this->numTriangles = 0; Mesh *m = header->mesh; int8 *adcbits = adc; for(uint32 i = 0; i < header->numMeshes; i++){ if(m->numIndices < 3){ // shouldn't happen but it does adcbits += m->numIndices; m++; continue; } if(header->flags == 1){ // tristrip for(uint32 j = 0; j < m->numIndices-2; j++){ if(!(adc && adcbits[j+2]) && !isDegenerate(&m->indices[j])) this->numTriangles++; } }else this->numTriangles += m->numIndices/3; adcbits += m->numIndices; m++; } delete[] this->triangles; this->triangles = new Triangle[this->numTriangles]; Triangle *tri = this->triangles; m = header->mesh; adcbits = adc; for(uint32 i = 0; i < header->numMeshes; i++){ if(m->numIndices < 3){ adcbits += m->numIndices; m++; continue; } int32 matid = findPointer((void*)m->material, (void**)this->materialList, this->numMaterials); if(header->flags == 1) // tristrip for(uint32 j = 0; j < m->numIndices-2; j++){ if(adc && adcbits[j+2] || isDegenerate(&m->indices[j])) continue; 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++; } else for(uint32 j = 0; j < m->numIndices-2; j+=3){ 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++; } adcbits += m->numIndices; m++; } } void Geometry::buildMeshes(void) { delete this->meshHeader; Triangle *tri; MeshHeader *h = new MeshHeader; this->meshHeader = h; if((this->geoflags & Geometry::TRISTRIP) == 0){ h->flags = 0; h->totalIndices = this->numTriangles*3; h->numMeshes = this->numMaterials; h->mesh = new Mesh[h->numMeshes]; for(uint32 i = 0; i < h->numMeshes; i++){ h->mesh[i].material = this->materialList[i]; h->mesh[i].numIndices = 0; } // count indices per mesh tri = this->triangles; for(int32 i = 0; i < this->numTriangles; i++){ h->mesh[tri->matId].numIndices += 3; tri++; } h->allocateIndices(); for(uint32 i = 0; i < h->numMeshes; i++) h->mesh[i].numIndices = 0; // same as above but fill with indices tri = this->triangles; for(int32 i = 0; i < this->numTriangles; i++){ uint32 idx = h->mesh[tri->matId].numIndices; h->mesh[tri->matId].indices[idx++] = tri->v[0]; h->mesh[tri->matId].indices[idx++] = tri->v[1]; h->mesh[tri->matId].indices[idx++] = tri->v[2]; h->mesh[tri->matId].numIndices = idx; tri++; } }else{ assert(0 && "can't tristrip\n"); } } // HAS to be called with an existing mesh void Geometry::removeUnusedMaterials(void) { if(this->meshHeader == nil) return; MeshHeader *mh = this->meshHeader; int32 *map = new int32[this->numMaterials]; Material **matlist = new Material*[this->numMaterials]; int32 numMaterials = 0; /* Build new material list and map */ for(uint32 i = 0; i < mh->numMeshes; i++){ Mesh *m = &mh->mesh[i]; if(m->numIndices <= 0) continue; matlist[numMaterials] = m->material; int32 oldid = findPointer((void*)m->material, (void**)this->materialList, this->numMaterials); map[oldid] = numMaterials; numMaterials++; } delete[] this->materialList; this->materialList = matlist; this->numMaterials = numMaterials; /* Build new meshes */ MeshHeader *newmh = new MeshHeader; newmh->flags = mh->flags; newmh->numMeshes = numMaterials; newmh->mesh = new Mesh[newmh->numMeshes]; newmh->totalIndices = mh->totalIndices; Mesh *newm = newmh->mesh; for(uint32 i = 0; i < mh->numMeshes; i++){ Mesh *oldm = &mh->mesh[i]; if(oldm->numIndices <= 0) continue; newm->numIndices = oldm->numIndices; newm->material = oldm->material; newm++; } newmh->allocateIndices(); /* Copy indices */ newm = newmh->mesh; for(uint32 i = 0; i < mh->numMeshes; i++){ Mesh *oldm = &mh->mesh[i]; if(oldm->numIndices <= 0) continue; memcpy(newm->indices, oldm->indices, oldm->numIndices*sizeof(*oldm->indices)); newm++; } delete this->meshHeader; this->meshHeader = newmh; /* Remap triangle material IDs */ for(int32 i = 0; i < this->numTriangles; i++) this->triangles[i].matId = map[this->triangles[i].matId]; delete[] map; } // // Material // Material* Material::create(void) { Material *mat = (Material*)malloc(s_plglist.size); if(mat == nil){ RWERROR((ERR_ALLOC, s_plglist.size)); return nil; } mat->texture = nil; memset(&mat->color, 0xFF, 4); mat->surfaceProps.ambient = 1.0f; mat->surfaceProps.specular = 1.0f; mat->surfaceProps.diffuse = 1.0f; mat->pipeline = nil; mat->refCount = 1; s_plglist.construct(mat); return mat; } Material* Material::clone(void) { Material *mat = Material::create(); if(mat == nil){ RWERROR((ERR_ALLOC, s_plglist.size)); return nil; } mat->color = this->color; mat->surfaceProps = this->surfaceProps; if(this->texture) mat->setTexture(this->texture); mat->pipeline = this->pipeline; s_plglist.copy(mat, this); return mat; } void Material::destroy(void) { this->refCount--; if(this->refCount <= 0){ s_plglist.destruct(this); if(this->texture) this->texture->destroy(); free(this); } } void Material::setTexture(Texture *tex) { if(this->texture) this->texture->destroy(); if(tex) tex->refCount++; this->texture = tex; } struct MatStreamData { int32 flags; // unused according to RW RGBA color; int32 unused; int32 textured; }; static uint32 materialRights[2]; Material* Material::streamRead(Stream *stream) { uint32 length, version; MatStreamData buf; 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; mat->color = buf.color; if(version < 0x30400){ mat->surfaceProps.ambient = 1.0f; mat->surfaceProps.specular = 1.0f; mat->surfaceProps.diffuse = 1.0f; }else{ float32 surfaceProps[3]; stream->read(surfaceProps, sizeof(surfaceProps)); mat->surfaceProps.ambient = surfaceProps[0]; mat->surfaceProps.specular = surfaceProps[1]; mat->surfaceProps.diffuse = surfaceProps[2]; } if(buf.textured){ if(!findChunk(stream, ID_TEXTURE, &length, nil)){ RWERROR((ERR_CHUNK, "TEXTURE")); goto fail; } Texture *t = Texture::streamRead(stream); if(t == nil) goto fail; mat->setTexture(t); } materialRights[0] = 0; if(!s_plglist.streamRead(stream, mat)) goto fail; if(materialRights[0]) s_plglist.assertRights(mat, materialRights[0], materialRights[1]); return mat; fail: mat->destroy(); return nil; } bool Material::streamWrite(Stream *stream) { MatStreamData buf; writeChunkHeader(stream, ID_MATERIAL, this->streamGetSize()); writeChunkHeader(stream, ID_STRUCT, sizeof(MatStreamData) + (rw::version >= 0x30400 ? 12 : 0)); buf.color = this->color; buf.flags = 0; buf.unused = 0; buf.textured = this->texture != nil; stream->write(&buf, sizeof(buf)); if(rw::version >= 0x30400){ float32 surfaceProps[3]; surfaceProps[0] = this->surfaceProps.ambient; surfaceProps[1] = this->surfaceProps.specular; surfaceProps[2] = this->surfaceProps.diffuse; stream->write(surfaceProps, sizeof(surfaceProps)); } if(this->texture) this->texture->streamWrite(stream); s_plglist.streamWrite(stream, this); return true; } uint32 Material::streamGetSize(void) { uint32 size = 0; size += 12 + sizeof(MatStreamData); if(rw::version >= 0x30400) size += 12; if(this->texture) size += 12 + this->texture->streamGetSize(); size += 12 + s_plglist.streamGetSize(this); return size; } // Material Rights plugin static Stream* readMaterialRights(Stream *stream, int32, void *, int32, int32) { stream->read(materialRights, 8); // printf("materialrights: %X %X\n", materialRights[0], materialRights[1]); return stream; } static Stream* writeMaterialRights(Stream *stream, int32, void *object, int32, int32) { Material *material = (Material*)object; uint32 buffer[2]; buffer[0] = material->pipeline->pluginID; buffer[1] = material->pipeline->pluginData; stream->write(buffer, 8); return stream; } static int32 getSizeMaterialRights(void *object, int32, int32) { Material *material = (Material*)object; if(material->pipeline == nil || material->pipeline->pluginID == 0) return 0; return 8; } void registerMaterialRightsPlugin(void) { Material::registerPlugin(0, ID_RIGHTTORENDER, nil, nil, nil); Material::registerPluginStream(ID_RIGHTTORENDER, readMaterialRights, writeMaterialRights, getSizeMaterialRights); } }