diff options
Diffstat (limited to 'src/d3d/d3d8.cpp')
| -rw-r--r-- | src/d3d/d3d8.cpp | 678 |
1 files changed, 678 insertions, 0 deletions
diff --git a/src/d3d/d3d8.cpp b/src/d3d/d3d8.cpp new file mode 100644 index 0000000..671948f --- /dev/null +++ b/src/d3d/d3d8.cpp @@ -0,0 +1,678 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#define WITH_D3D +#include "../rwbase.h" +#include "../rwerror.h" +#include "../rwplg.h" +#include "../rwpipeline.h" +#include "../rwobjects.h" +#include "../rwengine.h" +#include "rwd3d.h" +#include "rwd3d8.h" + +#include "rwd3dimpl.h" + +#define PLUGIN_ID 2 + +namespace rw { +namespace d3d8 { +using namespace d3d; + +static void* +driverOpen(void *o, int32, int32) +{ + engine->driver[PLATFORM_D3D8]->defaultPipeline = makeDefaultPipeline(); + + engine->driver[PLATFORM_D3D8]->rasterNativeOffset = nativeRasterOffset; + engine->driver[PLATFORM_D3D8]->rasterCreate = rasterCreate; + engine->driver[PLATFORM_D3D8]->rasterLock = rasterLock; + engine->driver[PLATFORM_D3D8]->rasterUnlock = rasterUnlock; + engine->driver[PLATFORM_D3D8]->rasterNumLevels = rasterNumLevels; + engine->driver[PLATFORM_D3D8]->imageFindRasterFormat = imageFindRasterFormat; + engine->driver[PLATFORM_D3D8]->rasterFromImage = rasterFromImage; + engine->driver[PLATFORM_D3D8]->rasterToImage = rasterToImage; + return o; +} + +static void* +driverClose(void *o, int32, int32) +{ + return o; +} + +void +registerPlatformPlugins(void) +{ + Driver::registerPlugin(PLATFORM_D3D8, 0, PLATFORM_D3D8, + driverOpen, driverClose); + // shared between D3D8 and 9 + if(nativeRasterOffset == 0) + registerNativeRaster(); +} + +uint32 +makeFVFDeclaration(uint32 flags, int32 numTex) +{ + uint32 fvf = 0x2; + if(flags & Geometry::NORMALS) + fvf |= 0x10; + if(flags & Geometry::PRELIT) + fvf |= 0x40; + fvf |= numTex << 8; + return fvf; +} + +int32 +getStride(uint32 flags, int32 numTex) +{ + int32 stride = 12; + if(flags & Geometry::NORMALS) + stride += 12;; + if(flags & Geometry::PRELIT) + stride += 4; + stride += numTex*8; + return stride; +} + +void* +destroyNativeData(void *object, int32, int32) +{ + Geometry *geometry = (Geometry*)object; + if(geometry->instData == nil || + geometry->instData->platform != PLATFORM_D3D8) + return object; + InstanceDataHeader *header = + (InstanceDataHeader*)geometry->instData; + geometry->instData = nil; + InstanceData *inst = header->inst; + for(uint32 i = 0; i < header->numMeshes; i++){ + destroyIndexBuffer(inst->indexBuffer); + destroyVertexBuffer(inst->vertexBuffer); + inst++; + } + rwFree(header->inst); + rwFree(header); + return object; +} + +Stream* +readNativeData(Stream *stream, int32, void *object, int32, int32) +{ + ASSERTLITTLE; + Geometry *geometry = (Geometry*)object; + uint32 platform; + if(!findChunk(stream, ID_STRUCT, nil, nil)){ + RWERROR((ERR_CHUNK, "STRUCT")); + return nil; + } + platform = stream->readU32(); + if(platform != PLATFORM_D3D8){ + RWERROR((ERR_PLATFORM, platform)); + return nil; + } + InstanceDataHeader *header = rwNewT(InstanceDataHeader, 1, MEMDUR_EVENT | ID_GEOMETRY); + geometry->instData = header; + header->platform = PLATFORM_D3D8; + + int32 size = stream->readI32(); + uint8 *data = rwNewT(uint8, size, MEMDUR_FUNCTION | ID_GEOMETRY); + stream->read8(data, size); + uint8 *p = data; + header->serialNumber = *(uint16*)p; p += 2; + header->numMeshes = *(uint16*)p; p += 2; + header->inst = rwNewT(InstanceData, header->numMeshes, MEMDUR_EVENT | ID_GEOMETRY); + + InstanceData *inst = header->inst; + for(uint32 i = 0; i < header->numMeshes; i++){ + inst->minVert = *(uint32*)p; p += 4; + inst->stride = *(uint32*)p; p += 4; + inst->numVertices = *(uint32*)p; p += 4; + inst->numIndices = *(uint32*)p; p += 4; + uint32 matid = *(uint32*)p; p += 4; + inst->material = geometry->matList.materials[matid]; + inst->vertexShader = *(uint32*)p; p += 4; + inst->primType = *(uint32*)p; p += 4; + inst->indexBuffer = nil; p += 4; + inst->vertexBuffer = nil; p += 4; + inst->baseIndex = 0; p += 4; + inst->vertexAlpha = *p++; + inst->managed = 0; p++; + inst->remapped = 0; p++; // TODO: really unused? and what's that anyway? + inst++; + } + rwFree(data); + + inst = header->inst; + for(uint32 i = 0; i < header->numMeshes; i++){ + assert(inst->indexBuffer == nil); + inst->indexBuffer = createIndexBuffer(inst->numIndices*2, false); + uint16 *indices = lockIndices(inst->indexBuffer, 0, 0, 0); + stream->read8(indices, 2*inst->numIndices); + unlockIndices(inst->indexBuffer); + + inst->managed = 1; + assert(inst->vertexBuffer == nil); + inst->vertexBuffer = createVertexBuffer(inst->stride*inst->numVertices, 0, false); + uint8 *verts = lockVertices(inst->vertexBuffer, 0, 0, D3DLOCK_NOSYSLOCK); + stream->read8(verts, inst->stride*inst->numVertices); + unlockVertices(inst->vertexBuffer); + + inst++; + } + return stream; +} + +Stream* +writeNativeData(Stream *stream, int32 len, void *object, int32, int32) +{ + ASSERTLITTLE; + Geometry *geometry = (Geometry*)object; + writeChunkHeader(stream, ID_STRUCT, len-12); + if(geometry->instData == nil || + geometry->instData->platform != PLATFORM_D3D8) + return stream; + stream->writeU32(PLATFORM_D3D8); + InstanceDataHeader *header = (InstanceDataHeader*)geometry->instData; + + int32 size = 4 + geometry->meshHeader->numMeshes*0x2C; + uint8 *data = rwNewT(uint8, size, MEMDUR_FUNCTION | ID_GEOMETRY); + stream->writeI32(size); + uint8 *p = data; + *(uint16*)p = header->serialNumber; p += 2; + *(uint16*)p = header->numMeshes; p += 2; + + InstanceData *inst = header->inst; + for(uint32 i = 0; i < header->numMeshes; i++){ + *(uint32*)p = inst->minVert; p += 4; + *(uint32*)p = inst->stride; p += 4; + *(uint32*)p = inst->numVertices; p += 4; + *(uint32*)p = inst->numIndices; p += 4; + int32 matid = geometry->matList.findIndex(inst->material); + *(int32*)p = matid; p += 4; + *(uint32*)p = inst->vertexShader; p += 4; + *(uint32*)p = inst->primType; p += 4; + *(uint32*)p = 0; p += 4; // index buffer + *(uint32*)p = 0; p += 4; // vertex buffer + *(uint32*)p = inst->baseIndex; p += 4; + *p++ = inst->vertexAlpha; + *p++ = inst->managed; + *p++ = inst->remapped; + inst++; + } + stream->write8(data, size); + rwFree(data); + + inst = header->inst; + for(uint32 i = 0; i < header->numMeshes; i++){ + uint16 *indices = lockIndices(inst->indexBuffer, 0, 0, 0); + stream->write8(indices, 2*inst->numIndices); + unlockIndices(inst->indexBuffer); + + uint8 *verts = lockVertices(inst->vertexBuffer, 0, 0, D3DLOCK_NOSYSLOCK); + stream->write8(verts, inst->stride*inst->numVertices); + unlockVertices(inst->vertexBuffer); + inst++; + } + return stream; +} + +int32 +getSizeNativeData(void *object, int32, int32) +{ + Geometry *geometry = (Geometry*)object; + if(geometry->instData == nil || + geometry->instData->platform != PLATFORM_D3D8) + return 0; + + InstanceDataHeader *header = (InstanceDataHeader*)geometry->instData; + InstanceData *inst = header->inst; + int32 size = 12 + 4 + 4 + 4 + header->numMeshes*0x2C; + for(int32 i = 0; i < header->numMeshes; i++){ + size += inst->numIndices*2 + inst->numVertices*inst->stride; + inst++; + } + return size; +} + +void +registerNativeDataPlugin(void) +{ + Geometry::registerPlugin(0, ID_NATIVEDATA, + nil, destroyNativeData, nil); + Geometry::registerPluginStream(ID_NATIVEDATA, + readNativeData, + writeNativeData, + getSizeNativeData); +} + + +static void +instance(rw::ObjPipeline *rwpipe, Atomic *atomic) +{ + ObjPipeline *pipe = (ObjPipeline*)rwpipe; + Geometry *geo = atomic->geometry; + // TODO: allow for REINSTANCE + if(geo->instData) + return; + InstanceDataHeader *header = rwNewT(InstanceDataHeader, 1, MEMDUR_EVENT | ID_GEOMETRY); + MeshHeader *meshh = geo->meshHeader; + geo->instData = header; + header->platform = PLATFORM_D3D8; + + header->serialNumber = meshh->serialNum; + header->numMeshes = meshh->numMeshes; + header->inst = rwNewT(InstanceData, header->numMeshes, MEMDUR_EVENT | ID_GEOMETRY); + + InstanceData *inst = header->inst; + Mesh *mesh = meshh->getMeshes(); + for(uint32 i = 0; i < header->numMeshes; i++){ + findMinVertAndNumVertices(mesh->indices, mesh->numIndices, + &inst->minVert, &inst->numVertices); + inst->numIndices = mesh->numIndices; + inst->material = mesh->material; + inst->vertexShader = 0; + inst->primType = meshh->flags == 1 ? D3DPT_TRIANGLESTRIP : D3DPT_TRIANGLELIST; + inst->vertexBuffer = nil; + inst->baseIndex = 0; // (maybe) not used by us + inst->vertexAlpha = 0; + inst->managed = 0; + inst->remapped = 0; + + inst->indexBuffer = createIndexBuffer(inst->numIndices*2, false); + uint16 *indices = lockIndices(inst->indexBuffer, 0, 0, 0); + if(inst->minVert == 0) + memcpy(indices, mesh->indices, inst->numIndices*2); + else + for(int32 j = 0; j < inst->numIndices; j++) + indices[j] = mesh->indices[j] - inst->minVert; + unlockIndices(inst->indexBuffer); + + pipe->instanceCB(geo, inst); + mesh++; + inst++; + } +} + +static void +uninstance(rw::ObjPipeline *rwpipe, Atomic *atomic) +{ + ObjPipeline *pipe = (ObjPipeline*)rwpipe; + Geometry *geo = atomic->geometry; + if((geo->flags & Geometry::NATIVE) == 0) + return; + assert(geo->instData != nil); + assert(geo->instData->platform == PLATFORM_D3D8); + geo->numTriangles = geo->meshHeader->guessNumTriangles(); + geo->allocateData(); + geo->allocateMeshes(geo->meshHeader->numMeshes, geo->meshHeader->totalIndices, 0); + + InstanceDataHeader *header = (InstanceDataHeader*)geo->instData; + InstanceData *inst = header->inst; + Mesh *mesh = geo->meshHeader->getMeshes(); + for(uint32 i = 0; i < header->numMeshes; i++){ + uint16 *indices = lockIndices(inst->indexBuffer, 0, 0, 0); + if(inst->minVert == 0) + memcpy(mesh->indices, indices, inst->numIndices*2); + else + for(int32 j = 0; j < inst->numIndices; j++) + mesh->indices[j] = indices[j] + inst->minVert; + unlockIndices(inst->indexBuffer); + + pipe->uninstanceCB(geo, inst); + mesh++; + inst++; + } + geo->generateTriangles(); + geo->flags &= ~Geometry::NATIVE; + destroyNativeData(geo, 0, 0); +} + +static void +render(rw::ObjPipeline *rwpipe, Atomic *atomic) +{ + ObjPipeline *pipe = (ObjPipeline*)rwpipe; + Geometry *geo = atomic->geometry; + // TODO: allow for REINSTANCE + if(geo->instData == nil) + pipe->instance(atomic); + assert(geo->instData != nil); + assert(geo->instData->platform == PLATFORM_D3D8); + if(pipe->renderCB) + pipe->renderCB(atomic, (InstanceDataHeader*)geo->instData); +} + +void +ObjPipeline::init(void) +{ + this->rw::ObjPipeline::init(PLATFORM_D3D8); + this->impl.instance = d3d8::instance; + this->impl.uninstance = d3d8::uninstance; + this->impl.render = d3d8::render; + this->instanceCB = nil; + this->uninstanceCB = nil; + this->renderCB = nil; +} + +ObjPipeline* +ObjPipeline::create(void) +{ + ObjPipeline *pipe = rwNewT(ObjPipeline, 1, MEMDUR_GLOBAL); + pipe->init(); + return pipe; +} + +void +defaultInstanceCB(Geometry *geo, InstanceData *inst) +{ + inst->vertexShader = makeFVFDeclaration(geo->flags, geo->numTexCoordSets); + inst->stride = getStride(geo->flags, geo->numTexCoordSets); + + assert(inst->vertexBuffer == nil); + inst->vertexBuffer = createVertexBuffer(inst->numVertices*inst->stride, + inst->vertexShader, false); + inst->managed = 1; + + uint8 *dst = lockVertices(inst->vertexBuffer, 0, 0, D3DLOCK_NOSYSLOCK); + instV3d(VERT_FLOAT3, dst, + &geo->morphTargets[0].vertices[inst->minVert], + inst->numVertices, inst->stride); + dst += 12; + + if(geo->flags & Geometry::NORMALS){ + instV3d(VERT_FLOAT3, dst, + &geo->morphTargets[0].normals[inst->minVert], + inst->numVertices, inst->stride); + dst += 12; + } + + inst->vertexAlpha = 0; + if(geo->flags & Geometry::PRELIT){ + inst->vertexAlpha = instColor(VERT_ARGB, dst, &geo->colors[inst->minVert], + inst->numVertices, inst->stride); + dst += 4; + } + + for(int32 i = 0; i < geo->numTexCoordSets; i++){ + instTexCoords(VERT_FLOAT2, dst, &geo->texCoords[i][inst->minVert], + inst->numVertices, inst->stride); + dst += 8; + } + unlockVertices(inst->vertexBuffer); +} + +void +defaultUninstanceCB(Geometry *geo, InstanceData *inst) +{ + uint8 *src = lockVertices(inst->vertexBuffer, 0, 0, D3DLOCK_NOSYSLOCK); + uninstV3d(VERT_FLOAT3, + &geo->morphTargets[0].vertices[inst->minVert], + src, inst->numVertices, inst->stride); + src += 12; + + if(geo->flags & Geometry::NORMALS){ + uninstV3d(VERT_FLOAT3, + &geo->morphTargets[0].normals[inst->minVert], + src, inst->numVertices, inst->stride); + src += 12; + } + + inst->vertexAlpha = 0; + if(geo->flags & Geometry::PRELIT){ + uninstColor(VERT_ARGB, &geo->colors[inst->minVert], src, + inst->numVertices, inst->stride); + src += 4; + } + + for(int32 i = 0; i < geo->numTexCoordSets; i++){ + uninstTexCoords(VERT_FLOAT2, &geo->texCoords[i][inst->minVert], src, + inst->numVertices, inst->stride); + src += 8; + } + unlockVertices(inst->vertexBuffer); +} + +ObjPipeline* +makeDefaultPipeline(void) +{ + ObjPipeline *pipe = ObjPipeline::create(); + pipe->instanceCB = defaultInstanceCB; + pipe->uninstanceCB = defaultUninstanceCB; + pipe->renderCB = defaultRenderCB; + return pipe; +} + +// Native Texture and Raster + +// only handles 4 and 8 bit textures right now +Raster* +readAsImage(Stream *stream, int32 width, int32 height, int32 depth, int32 format, int32 numLevels) +{ + uint8 palette[256*4]; + int32 pallen = 0; + uint8 *data = nil; + + Image *img = Image::create(width, height, 32); + img->allocate(); + + if(format & Raster::PAL4){ + pallen = 16; + stream->read8(palette, 4*32); + }else if(format & Raster::PAL8){ + pallen = 256; + stream->read8(palette, 4*256); + } + if(!Raster::formatHasAlpha(format)) + for(int32 i = 0; i < pallen; i++) + palette[i*4+3] = 0xFF; + + Raster *ras = nil; + + for(int i = 0; i < numLevels; i++){ + uint32 size = stream->readU32(); + + // don't read levels that don't exist + if(ras && i >= ras->getNumLevels()){ + stream->seek(size); + continue; + } + + // one allocation is enough, first level is largest + if(data == nil) + data = rwNewT(uint8, size, MEMDUR_FUNCTION | ID_IMAGE); + stream->read8(data, size); + + if(ras){ + ras->lock(i, Raster::LOCKWRITE|Raster::LOCKNOFETCH); + img->width = ras->width; + img->height = ras->height; + img->stride = img->width*img->bpp; + } + + if(format & (Raster::PAL4 | Raster::PAL8)){ + uint8 *idx = data; + uint8 *pixels = img->pixels; + for(int y = 0; y < img->height; y++){ + uint8 *line = pixels; + for(int x = 0; x < img->width; x++){ + line[0] = palette[*idx*4+0]; + line[1] = palette[*idx*4+1]; + line[2] = palette[*idx*4+2]; + if(img->bpp > 3) + line[3] = palette[*idx*4+3]; + line += img->bpp; + idx++; + } + pixels += img->stride; + } + } + + if(ras == nil){ + // Important to have filled the image with data + int32 newformat; + Raster::imageFindRasterFormat(img, format&7, &width, &height, &depth, &newformat); + newformat |= format & (Raster::MIPMAP | Raster::AUTOMIPMAP); + ras = Raster::create(width, height, depth, newformat); + ras->lock(i, Raster::LOCKWRITE|Raster::LOCKNOFETCH); + } + + ras->setFromImage(img); + ras->unlock(i); + } + + rwFree(data); + img->destroy(); + return ras; +} + +Texture* +readNativeTexture(Stream *stream) +{ + uint32 platform; + if(!findChunk(stream, ID_STRUCT, nil, nil)){ + RWERROR((ERR_CHUNK, "STRUCT")); + return nil; + } + platform = stream->readU32(); + if(platform != PLATFORM_D3D8){ + RWERROR((ERR_PLATFORM, platform)); + return nil; + } + Texture *tex = Texture::create(nil); + if(tex == nil) + return nil; + + // Texture + tex->filterAddressing = stream->readU32(); + stream->read8(tex->name, 32); + stream->read8(tex->mask, 32); + + // Raster + uint32 format = stream->readU32(); + bool32 hasAlpha = stream->readI32(); + int32 width = stream->readU16(); + int32 height = stream->readU16(); + int32 depth = stream->readU8(); + int32 numLevels = stream->readU8(); + int32 type = stream->readU8(); + int32 compression = stream->readU8(); + + int32 pallength = 0; + if(format & Raster::PAL4 || format & Raster::PAL8){ + pallength = format & Raster::PAL4 ? 32 : 256; + if(!d3d::isP8supported){ + tex->raster = readAsImage(stream, width, height, depth, format|type, numLevels); + return tex; + } + } + + Raster *raster; + D3dRaster *ras; + if(compression){ + raster = Raster::create(width, height, depth, format | type | Raster::DONTALLOCATE, PLATFORM_D3D8); + ras = GETD3DRASTEREXT(raster); + allocateDXT(raster, compression, numLevels, hasAlpha); + ras->customFormat = 1; + }else{ + raster = Raster::create(width, height, depth, format | type, PLATFORM_D3D8); + ras = GETD3DRASTEREXT(raster); + } + tex->raster = raster; + + // TODO: check if format supported and convert if necessary + + if(pallength != 0) + stream->read8(ras->palette, 4*pallength); + + uint32 size; + uint8 *data; + for(int32 i = 0; i < numLevels; i++){ + size = stream->readU32(); + if(i < raster->getNumLevels()){ + data = raster->lock(i, Raster::LOCKWRITE|Raster::LOCKNOFETCH); + stream->read8(data, size); + raster->unlock(i); + }else + stream->seek(size); + } + return tex; +} + +void +writeNativeTexture(Texture *tex, Stream *stream) +{ + int32 chunksize = getSizeNativeTexture(tex); + writeChunkHeader(stream, ID_STRUCT, chunksize-12); + stream->writeU32(PLATFORM_D3D8); + + // Texture + stream->writeU32(tex->filterAddressing); + stream->write8(tex->name, 32); + stream->write8(tex->mask, 32); + + // Raster + Raster *raster = tex->raster; + D3dRaster *ras = GETD3DRASTEREXT(raster); + int32 numLevels = raster->getNumLevels(); + stream->writeI32(raster->format); + stream->writeI32(ras->hasAlpha); + stream->writeU16(raster->width); + stream->writeU16(raster->height); + stream->writeU8(raster->depth); + stream->writeU8(numLevels); + stream->writeU8(raster->type); + int32 compression = 0; + if(ras->format) + switch(ras->format){ + case 0x31545844: // DXT1 + compression = 1; + break; + case 0x32545844: // DXT2 + compression = 2; + break; + case 0x33545844: // DXT3 + compression = 3; + break; + case 0x34545844: // DXT4 + compression = 4; + break; + case 0x35545844: // DXT5 + compression = 5; + break; + } + stream->writeU8(compression); + + if(raster->format & Raster::PAL4) + stream->write8(ras->palette, 4*32); + else if(raster->format & Raster::PAL8) + stream->write8(ras->palette, 4*256); + + uint32 size; + uint8 *data; + for(int32 i = 0; i < numLevels; i++){ + size = getLevelSize(raster, i); + stream->writeU32(size); + data = raster->lock(i, Raster::LOCKREAD); + stream->write8(data, size); + raster->unlock(i); + } +} + +uint32 +getSizeNativeTexture(Texture *tex) +{ + uint32 size = 12 + 72 + 16; + int32 levels = tex->raster->getNumLevels(); + if(tex->raster->format & Raster::PAL4) + size += 4*32; + else if(tex->raster->format & Raster::PAL8) + size += 4*256; + for(int32 i = 0; i < levels; i++) + size += 4 + getLevelSize(tex->raster, i); + return size; +} + +} +} |
