diff --git a/Makefile b/Makefile index 30f9da6..75355c1 100644 --- a/Makefile +++ b/Makefile @@ -28,6 +28,6 @@ $(BUILDDIR)/%.d: $(SRCDIR)/%.cpp clean: echo $(SRC) - rm -f $(BUILDDIR)/*.[od] + rm -rf $(BUILDDIR)/* -include $(DEP) diff --git a/rw.h b/rw.h index f4496f6..ffc88a6 100644 --- a/rw.h +++ b/rw.h @@ -15,3 +15,5 @@ #include "src/d3d/rwd3d8.h" #include "src/d3d/rwd3d9.h" #include "src/gl/rwwdgl.h" +#include "src/gl/rwgl3.h" +#include "src/gl/rwgl3shader.h" diff --git a/src/d3d/d3d.cpp b/src/d3d/d3d.cpp new file mode 100644 index 0000000..5ebac1a --- /dev/null +++ b/src/d3d/d3d.cpp @@ -0,0 +1,613 @@ +#include +#include +#include +#include + +#include "../rwbase.h" +#include "../rwplg.h" +#include "../rwpipeline.h" +#include "../rwobjects.h" +#include "../rwengine.h" +#include "rwd3d.h" + +namespace rw { +namespace d3d { + +bool32 isP8supported = 1; + +#ifdef RW_D3D9 +IDirect3DDevice9 *device = nil; +#else +#define MAKEFOURCC(ch0, ch1, ch2, ch3) \ + ((uint32)(uint8)(ch0) | ((uint32)(uint8)(ch1) << 8) | \ + ((uint32)(uint8)(ch2) << 16) | ((uint32)(uint8)(ch3) << 24 )) +enum { + D3DFMT_UNKNOWN = 0, + + D3DFMT_R8G8B8 = 20, + D3DFMT_A8R8G8B8 = 21, + D3DFMT_X8R8G8B8 = 22, + D3DFMT_R5G6B5 = 23, + D3DFMT_X1R5G5B5 = 24, + D3DFMT_A1R5G5B5 = 25, + D3DFMT_A4R4G4B4 = 26, + D3DFMT_R3G3B2 = 27, + D3DFMT_A8 = 28, + D3DFMT_A8R3G3B2 = 29, + D3DFMT_X4R4G4B4 = 30, + D3DFMT_A2B10G10R10 = 31, + D3DFMT_A8B8G8R8 = 32, + D3DFMT_X8B8G8R8 = 33, + D3DFMT_G16R16 = 34, + D3DFMT_A2R10G10B10 = 35, + D3DFMT_A16B16G16R16 = 36, + + D3DFMT_A8P8 = 40, + D3DFMT_P8 = 41, + + D3DFMT_L8 = 50, + D3DFMT_A8L8 = 51, + D3DFMT_A4L4 = 52, + + D3DFMT_V8U8 = 60, + D3DFMT_L6V5U5 = 61, + D3DFMT_X8L8V8U8 = 62, + D3DFMT_Q8W8V8U8 = 63, + D3DFMT_V16U16 = 64, + D3DFMT_A2W10V10U10 = 67, + + D3DFMT_UYVY = MAKEFOURCC('U', 'Y', 'V', 'Y'), + D3DFMT_R8G8_B8G8 = MAKEFOURCC('R', 'G', 'B', 'G'), + D3DFMT_YUY2 = MAKEFOURCC('Y', 'U', 'Y', '2'), + D3DFMT_G8R8_G8B8 = MAKEFOURCC('G', 'R', 'G', 'B'), + D3DFMT_DXT1 = MAKEFOURCC('D', 'X', 'T', '1'), + D3DFMT_DXT2 = MAKEFOURCC('D', 'X', 'T', '2'), + D3DFMT_DXT3 = MAKEFOURCC('D', 'X', 'T', '3'), + D3DFMT_DXT4 = MAKEFOURCC('D', 'X', 'T', '4'), + D3DFMT_DXT5 = MAKEFOURCC('D', 'X', 'T', '5'), + + D3DFMT_D16_LOCKABLE = 70, + D3DFMT_D32 = 71, + D3DFMT_D15S1 = 73, + D3DFMT_D24S8 = 75, + D3DFMT_D24X8 = 77, + D3DFMT_D24X4S4 = 79, + D3DFMT_D16 = 80, + + D3DFMT_D32F_LOCKABLE = 82, + D3DFMT_D24FS8 = 83, + + // d3d9ex only + /* Z-Stencil formats valid for CPU access */ + D3DFMT_D32_LOCKABLE = 84, + D3DFMT_S8_LOCKABLE = 85, + + D3DFMT_L16 = 81, + + D3DFMT_VERTEXDATA =100, + D3DFMT_INDEX16 =101, + D3DFMT_INDEX32 =102, + + D3DFMT_Q16W16V16U16 =110, + + D3DFMT_MULTI2_ARGB8 = MAKEFOURCC('M','E','T','1'), + + // Floating point surface formats + + // s10e5 formats (16-bits per channel) + D3DFMT_R16F = 111, + D3DFMT_G16R16F = 112, + D3DFMT_A16B16G16R16F = 113, + + // IEEE s23e8 formats (32-bits per channel) + D3DFMT_R32F = 114, + D3DFMT_G32R32F = 115, + D3DFMT_A32B32G32R32F = 116, + + D3DFMT_CxV8U8 = 117, + + // d3d9ex only + // Monochrome 1 bit per pixel format + D3DFMT_A1 = 118, + // 2.8 biased fixed point + D3DFMT_A2B10G10R10_XR_BIAS = 119, + // Binary format indicating that the data has no inherent type + D3DFMT_BINARYBUFFER = 199, +}; +#endif + +// stolen from d3d8to9 +static uint32 +calculateTextureSize(uint32 width, uint32 height, uint32 depth, uint32 format) +{ +#define D3DFMT_W11V11U10 65 + switch(format){ + default: + case D3DFMT_UNKNOWN: + return 0; + case D3DFMT_R3G3B2: + case D3DFMT_A8: + case D3DFMT_P8: + case D3DFMT_L8: + case D3DFMT_A4L4: + return width * height * depth; + case D3DFMT_R5G6B5: + case D3DFMT_X1R5G5B5: + case D3DFMT_A1R5G5B5: + case D3DFMT_A4R4G4B4: + case D3DFMT_A8R3G3B2: + case D3DFMT_X4R4G4B4: + case D3DFMT_A8P8: + case D3DFMT_A8L8: + case D3DFMT_V8U8: + case D3DFMT_L6V5U5: + case D3DFMT_D16_LOCKABLE: + case D3DFMT_D15S1: + case D3DFMT_D16: + case D3DFMT_UYVY: + case D3DFMT_YUY2: + return width * 2 * height * depth; + case D3DFMT_R8G8B8: + return width * 3 * height * depth; + case D3DFMT_A8R8G8B8: + case D3DFMT_X8R8G8B8: + case D3DFMT_A2B10G10R10: + case D3DFMT_A8B8G8R8: + case D3DFMT_X8B8G8R8: + case D3DFMT_G16R16: + case D3DFMT_X8L8V8U8: + case D3DFMT_Q8W8V8U8: + case D3DFMT_V16U16: + case D3DFMT_W11V11U10: + case D3DFMT_A2W10V10U10: + case D3DFMT_D32: + case D3DFMT_D24S8: + case D3DFMT_D24X8: + case D3DFMT_D24X4S4: + return width * 4 * height * depth; + case D3DFMT_DXT1: + assert(depth <= 1); + return ((width + 3) >> 2) * ((height + 3) >> 2) * 8; + case D3DFMT_DXT2: + case D3DFMT_DXT3: + case D3DFMT_DXT4: + case D3DFMT_DXT5: + assert(depth <= 1); + return ((width + 3) >> 2) * ((height + 3) >> 2) * 16; + } +} + +int vertFormatMap[] = { + -1, VERT_FLOAT2, VERT_FLOAT3, -1, VERT_ARGB +}; + +void* +createIndexBuffer(uint32 length) +{ +#ifdef RW_D3D9 + IDirect3DIndexBuffer9 *ibuf; + device->CreateIndexBuffer(length, D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_MANAGED, &ibuf, 0); + return ibuf; +#else + return new uint8[length]; +#endif +} + +uint16* +lockIndices(void *indexBuffer, uint32 offset, uint32 size, uint32 flags) +{ + if(indexBuffer == nil) + return nil; +#ifdef RW_D3D9 + uint16 *indices; + IDirect3DIndexBuffer9 *ibuf = (IDirect3DIndexBuffer9*)indexBuffer; + ibuf->Lock(offset, size, (void**)&indices, flags); + return indices; +#else + (void)offset; + (void)size; + (void)flags; + return (uint16*)indexBuffer; +#endif +} + +void +unlockIndices(void *indexBuffer) +{ + if(indexBuffer == nil) + return; +#ifdef RW_D3D9 + IDirect3DIndexBuffer9 *ibuf = (IDirect3DIndexBuffer9*)indexBuffer; + ibuf->Unlock(); +#endif +} + +void* +createVertexBuffer(uint32 length, uint32 fvf, int32 pool) +{ +#ifdef RW_D3D9 + IDirect3DVertexBuffer9 *vbuf; + device->CreateVertexBuffer(length, D3DUSAGE_WRITEONLY, fvf, (D3DPOOL)pool, &vbuf, 0); + return vbuf; +#else + (void)fvf; + (void)pool; + return new uint8[length]; +#endif +} + +uint8* +lockVertices(void *vertexBuffer, uint32 offset, uint32 size, uint32 flags) +{ + if(vertexBuffer == nil) + return nil; +#ifdef RW_D3D9 + uint8 *verts; + IDirect3DVertexBuffer9 *vertbuf = (IDirect3DVertexBuffer9*)vertexBuffer; + vertbuf->Lock(offset, size, (void**)&verts, flags); + return verts; +#else + (void)offset; + (void)size; + (void)flags; + return (uint8*)vertexBuffer; +#endif +} + +void +unlockVertices(void *vertexBuffer) +{ + if(vertexBuffer == nil) + return; +#ifdef RW_D3D9 + IDirect3DVertexBuffer9 *vertbuf = (IDirect3DVertexBuffer9*)vertexBuffer; + vertbuf->Unlock(); +#endif +} + +void* +createTexture(int32 width, int32 height, int32 numlevels, uint32 format) +{ +#ifdef RW_D3D9 + IDirect3DTexture9 *tex; + device->CreateTexture(width, height, numlevels, 0, + (D3DFORMAT)format, D3DPOOL_MANAGED, &tex, nil); + return tex; +#else + int32 w = width; + int32 h = height; + int32 size = 0; + for(int32 i = 0; i < numlevels; i++){ + size += calculateTextureSize(w, h, 1, format); + w /= 2; + if(w == 0) w = 1; + h /= 2; + if(h == 0) h = 1; + } + uint8 *data = new uint8[sizeof(RasterLevels)+sizeof(RasterLevels::Level)*(numlevels-1)+size]; + RasterLevels *levels = (RasterLevels*)data; + data += sizeof(RasterLevels)+sizeof(RasterLevels::Level)*(numlevels-1); + levels->numlevels = numlevels; + levels->format = format; + w = width; + h = height; + for(int32 i = 0; i < numlevels; i++){ + levels->levels[i].width = w; + levels->levels[i].height = h; + levels->levels[i].data = data; + levels->levels[i].size = calculateTextureSize(w, h, 1, format); + data += levels->levels[i].size; + w /= 2; + if(w == 0) w = 1; + h /= 2; + if(h == 0) h = 1; + } + return levels; +#endif +} + +uint8* +lockTexture(void *texture, int32 level) +{ +#ifdef RW_D3D9 + IDirect3DTexture9 *tex = (IDirect3DTexture9*)texture; + D3DLOCKED_RECT lr; + tex->LockRect(level, &lr, 0, 0); + return (uint8*)lr.pBits; +#else + RasterLevels *levels = (RasterLevels*)texture; + return levels->levels[level].data; +#endif +} + +void +unlockTexture(void *texture, int32 level) +{ + (void)texture; + (void)level; +#ifdef RW_D3D9 + IDirect3DTexture9 *tex = (IDirect3DTexture9*)texture; + tex->UnlockRect(level); +#endif +} + +void +deleteObject(void *object) +{ + if(object == nil) + return; +#ifdef RW_D3D9 + IUnknown *unk = (IUnknown*)object; + unk->Release(); +#else + delete[] (uint*)object; +#endif +} + +// Native Raster + +int32 nativeRasterOffset; + +static void +rasterCreate(Raster *raster) +{ + D3dRaster *natras = PLUGINOFFSET(D3dRaster, raster, nativeRasterOffset); + static uint32 formatMap[] = { + 0, + D3DFMT_A1R5G5B5, + D3DFMT_R5G6B5, + D3DFMT_A4R4G4B4, + D3DFMT_L8, + D3DFMT_A8R8G8B8, + D3DFMT_X8R8G8B8, + 0, 0, 0, + D3DFMT_X1R5G5B5, + 0, 0, 0, 0, 0 + }; + static bool32 alphaMap[] = { + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 0, 0, 0, + 0, + 0, 0, 0, 0, 0 + }; + if(raster->flags & 0x80) + return; + uint32 format; + if(raster->format & (Raster::PAL4 | Raster::PAL8)){ + format = D3DFMT_P8; + natras->palette = new uint8[4*256]; + }else + format = formatMap[(raster->format >> 8) & 0xF]; + natras->format = 0; + natras->hasAlpha = alphaMap[(raster->format >> 8) & 0xF]; + int32 levels = Raster::calculateNumLevels(raster->width, raster->height); + natras->texture = createTexture(raster->width, raster->height, + raster->format & Raster::MIPMAP ? levels : 1, + format); +} + +static uint8* +rasterLock(Raster *raster, int32 level) +{ + D3dRaster *natras = PLUGINOFFSET(D3dRaster, raster, nativeRasterOffset); + return lockTexture(natras->texture, level); +} + +static void +rasterUnlock(Raster *raster, int32 level) +{ + D3dRaster *natras = PLUGINOFFSET(D3dRaster, raster, nativeRasterOffset); + unlockTexture(natras->texture, level); +} + +static int32 +rasterNumLevels(Raster *raster) +{ + D3dRaster *natras = PLUGINOFFSET(D3dRaster, raster, nativeRasterOffset); +#ifdef RW_D3D9 + IDirect3DTexture9 *tex = (IDirect3DTexture9*)natras->texture; + return tex->GetLevelCount(); +#else + RasterLevels *levels = (RasterLevels*)natras->texture; + return levels->numlevels; +#endif +} + +static void +rasterFromImage(Raster *raster, Image *image) +{ + int32 format; + D3dRaster *natras = PLUGINOFFSET(D3dRaster, raster, nativeRasterOffset); + switch(image->depth){ + case 32: + format = image->hasAlpha() ? Raster::C8888 : Raster::C888; + break; + case 24: + format = Raster::C888; + break; + case 16: + format = Raster::C1555; + break; + case 8: + format = Raster::PAL8 | Raster::C8888; + break; + case 4: + format = Raster::PAL4 | Raster::C8888; + break; + default: + return; + } + format |= 4; + + raster->type = format & 0x7; + raster->flags = format & 0xF8; + raster->format = format & 0xFF00; + rasterCreate(raster); + + uint8 *in, *out; + int pallength = 0; + if(raster->format & Raster::PAL4) + pallength = 16; + else if(raster->format & Raster::PAL8) + pallength = 256; + if(pallength){ + in = image->palette; + out = (uint8*)natras->palette; + for(int32 i = 0; i < pallength; i++){ + out[0] = in[2]; + out[1] = in[1]; + out[2] = in[0]; + out[3] = in[3]; + in += 4; + out += 4; + } + } + + int32 inc = image->depth/8; + in = image->pixels; + out = raster->lock(0); + if(pallength) + memcpy(out, in, raster->width*raster->height); + else + // TODO: stride + for(int32 y = 0; y < image->height; y++) + for(int32 x = 0; x < image->width; x++) + switch(raster->format & 0xF00){ + case Raster::C8888: + out[0] = in[2]; + out[1] = in[1]; + out[2] = in[0]; + out[3] = in[3]; + in += inc; + out += 4; + break; + case Raster::C888: + out[0] = in[2]; + out[1] = in[1]; + out[2] = in[0]; + out[3] = 0xFF; + in += inc; + out += 4; + break; + case Raster::C1555: + out[0] = in[0]; + out[1] = in[1]; + in += 2; + out += 2; + break; + } + raster->unlock(0); +} + +int32 +getLevelSize(Raster *raster, int32 level) +{ + D3dRaster *ras = PLUGINOFFSET(D3dRaster, raster, nativeRasterOffset); +#ifdef RW_D3D9 + IDirect3DTexture9 *tex = (IDirect3DTexture9*)ras->texture; + D3DSURFACE_DESC desc; + tex->GetLevelDesc(level, &desc); + return calculateTextureSize(desc.Width, desc.Height, 1, desc.Format); +#else + RasterLevels *levels = (RasterLevels*)ras->texture; + return levels->levels[level].size; +#endif +} + +void +allocateDXT(Raster *raster, int32 dxt, int32 numLevels, bool32 hasAlpha) +{ + static uint32 dxtMap[] = { + 0x31545844, // DXT1 + 0x32545844, // DXT2 + 0x33545844, // DXT3 + 0x34545844, // DXT4 + 0x35545844, // DXT5 + }; + D3dRaster *ras = PLUGINOFFSET(D3dRaster, raster, nativeRasterOffset); + ras->format = dxtMap[dxt-1]; + ras->hasAlpha = hasAlpha; + ras->texture = createTexture(raster->width, raster->height, + raster->format & Raster::MIPMAP ? numLevels : 1, + ras->format); + raster->flags &= ~0x80; +} + +void +setPalette(Raster *raster, void *palette, int32 size) +{ + D3dRaster *ras = PLUGINOFFSET(D3dRaster, raster, nativeRasterOffset); + memcpy(ras->palette, palette, 4*size); +} + +void +setTexels(Raster *raster, void *texels, int32 level) +{ + uint8 *dst = raster->lock(level); + memcpy(dst, texels, getLevelSize(raster, level)); + raster->unlock(level); +} + +static void* +createNativeRaster(void *object, int32 offset, int32) +{ + D3dRaster *raster = PLUGINOFFSET(D3dRaster, object, offset); + raster->texture = nil; + raster->palette = nil; + raster->format = 0; + raster->hasAlpha = 0; + raster->customFormat = 0; + return object; +} + +static void* +destroyNativeRaster(void *object, int32 offset, int32) +{ + // TODO: + (void)offset; + return object; +} + +static void* +copyNativeRaster(void *dst, void *, int32 offset, int32) +{ + D3dRaster *raster = PLUGINOFFSET(D3dRaster, dst, offset); + raster->texture = nil; + raster->palette = nil; + raster->format = 0; + raster->hasAlpha = 0; + raster->customFormat = 0; + return dst; +} + +void +registerNativeRaster(void) +{ + nativeRasterOffset = Raster::registerPlugin(sizeof(D3dRaster), + 0x12340000 | PLATFORM_D3D9, + createNativeRaster, + destroyNativeRaster, + copyNativeRaster); + driver[PLATFORM_D3D8].rasterNativeOffset = nativeRasterOffset; + driver[PLATFORM_D3D8].rasterCreate = rasterCreate; + driver[PLATFORM_D3D8].rasterLock = rasterLock; + driver[PLATFORM_D3D8].rasterUnlock = rasterUnlock; + driver[PLATFORM_D3D8].rasterNumLevels = rasterNumLevels; + driver[PLATFORM_D3D8].rasterFromImage = rasterFromImage; + + driver[PLATFORM_D3D9].rasterNativeOffset = nativeRasterOffset; + driver[PLATFORM_D3D9].rasterCreate = rasterCreate; + driver[PLATFORM_D3D9].rasterLock = rasterLock; + driver[PLATFORM_D3D9].rasterUnlock = rasterUnlock; + driver[PLATFORM_D3D9].rasterNumLevels = rasterNumLevels; + driver[PLATFORM_D3D9].rasterFromImage = rasterFromImage; +} + +} +} diff --git a/src/d3d/d3d8.cpp b/src/d3d/d3d8.cpp new file mode 100644 index 0000000..d3ab9bc --- /dev/null +++ b/src/d3d/d3d8.cpp @@ -0,0 +1,630 @@ +#include +#include +#include +#include + +#include "../rwbase.h" +#include "../rwerror.h" +#include "../rwplg.h" +#include "../rwpipeline.h" +#include "../rwobjects.h" +#include "../rwengine.h" +#include "rwd3d.h" +#include "rwd3d8.h" + +#define PLUGIN_ID 2 + +namespace rw { +namespace d3d8 { +using namespace d3d; + +void +initializePlatform(void) +{ + driver[PLATFORM_D3D8].defaultPipeline = makeDefaultPipeline(); +} + +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++){ + deleteObject(inst->indexBuffer); + deleteObject(inst->vertexBuffer); + inst++; + } + delete[] header->inst; + delete header; + return object; +} + +Stream* +readNativeData(Stream *stream, int32, void *object, int32, int32) +{ + 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 = new InstanceDataHeader; + geometry->instData = header; + header->platform = PLATFORM_D3D8; + + int32 size = stream->readI32(); + uint8 *data = new uint8[size]; + stream->read(data, size); + uint8 *p = data; + header->serialNumber = *(uint16*)p; p += 2; + header->numMeshes = *(uint16*)p; p += 2; + header->inst = new InstanceData[header->numMeshes]; + + 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->materialList[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++; + } + delete[] data; + + inst = header->inst; + for(uint32 i = 0; i < header->numMeshes; i++){ + inst->indexBuffer = createIndexBuffer(inst->numIndices*2); + uint16 *indices = lockIndices(inst->indexBuffer, 0, 0, 0); + stream->read(indices, 2*inst->numIndices); + unlockIndices(inst->indexBuffer); + + inst->managed = 1; + inst->vertexBuffer = createVertexBuffer(inst->stride*inst->numVertices, 0, D3DPOOL_MANAGED); + uint8 *verts = lockVertices(inst->vertexBuffer, 0, 0, D3DLOCK_NOSYSLOCK); + stream->read(verts, inst->stride*inst->numVertices); + unlockVertices(inst->vertexBuffer); + + inst++; + } + return stream; +} + +Stream* +writeNativeData(Stream *stream, int32 len, void *object, int32, int32) +{ + 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 = new uint8[size]; + 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 = findPointer(inst->material, (void**)geometry->materialList, geometry->numMaterials); + *(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->write(data, size); + delete[] data; + + inst = header->inst; + for(uint32 i = 0; i < header->numMeshes; i++){ + uint16 *indices = lockIndices(inst->indexBuffer, 0, 0, 0); + stream->write(indices, 2*inst->numIndices); + unlockIndices(inst->indexBuffer); + + uint8 *verts = lockVertices(inst->vertexBuffer, 0, 0, D3DLOCK_NOSYSLOCK); + stream->write(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; + if(geo->geoflags & Geometry::NATIVE) + return; + geo->geoflags |= Geometry::NATIVE; + InstanceDataHeader *header = new InstanceDataHeader; + MeshHeader *meshh = geo->meshHeader; + geo->instData = header; + header->platform = PLATFORM_D3D8; + + header->serialNumber = 0; + header->numMeshes = meshh->numMeshes; + header->inst = new InstanceData[header->numMeshes]; + + InstanceData *inst = header->inst; + Mesh *mesh = meshh->mesh; + 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); + 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->geoflags & Geometry::NATIVE) == 0) + return; + assert(geo->instData != nil); + assert(geo->instData->platform == PLATFORM_D3D8); + geo->geoflags &= ~Geometry::NATIVE; + geo->allocateData(); + geo->meshHeader->allocateIndices(); + + InstanceDataHeader *header = (InstanceDataHeader*)geo->instData; + InstanceData *inst = header->inst; + Mesh *mesh = geo->meshHeader->mesh; + 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(); + destroyNativeData(geo, 0, 0); +} + +static void +render(rw::ObjPipeline *rwpipe, Atomic *atomic) +{ + ObjPipeline *pipe = (ObjPipeline*)rwpipe; + Geometry *geo = atomic->geometry; + if((geo->geoflags & Geometry::NATIVE) == 0) + pipe->instance(atomic); + assert(geo->instData != nil); + assert(geo->instData->platform == PLATFORM_D3D8); + if(pipe->renderCB) + pipe->renderCB(atomic, (InstanceDataHeader*)geo->instData); +} + +ObjPipeline::ObjPipeline(uint32 platform) + : rw::ObjPipeline(platform) +{ + this->impl.instance = d3d8::instance; + this->impl.uninstance = d3d8::uninstance; + this->impl.render = d3d8::render; + this->instanceCB = nil; + this->uninstanceCB = nil; + this->renderCB = nil; +} + +void +defaultInstanceCB(Geometry *geo, InstanceData *inst) +{ + inst->vertexShader = makeFVFDeclaration(geo->geoflags, geo->numTexCoordSets); + inst->stride = getStride(geo->geoflags, geo->numTexCoordSets); + + inst->vertexBuffer = createVertexBuffer(inst->numVertices*inst->stride, + inst->vertexShader, D3DPOOL_MANAGED); + inst->managed = 1; + + uint8 *dst = lockVertices(inst->vertexBuffer, 0, 0, D3DLOCK_NOSYSLOCK); + instV3d(VERT_FLOAT3, dst, + &geo->morphTargets[0].vertices[3*inst->minVert], + inst->numVertices, inst->stride); + dst += 12; + + if(geo->geoflags & Geometry::NORMALS){ + instV3d(VERT_FLOAT3, dst, + &geo->morphTargets[0].normals[3*inst->minVert], + inst->numVertices, inst->stride); + dst += 12; + } + + inst->vertexAlpha = 0; + if(geo->geoflags & Geometry::PRELIT){ + inst->vertexAlpha = instColor(VERT_ARGB, dst, &geo->colors[4*inst->minVert], + inst->numVertices, inst->stride); + dst += 4; + } + + for(int32 i = 0; i < geo->numTexCoordSets; i++){ + instV2d(VERT_FLOAT2, dst, &geo->texCoords[i][2*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[3*inst->minVert], + src, inst->numVertices, inst->stride); + src += 12; + + if(geo->geoflags & Geometry::NORMALS){ + uninstV3d(VERT_FLOAT3, + &geo->morphTargets[0].normals[3*inst->minVert], + src, inst->numVertices, inst->stride); + src += 12; + } + + inst->vertexAlpha = 0; + if(geo->geoflags & Geometry::PRELIT){ + uninstColor(VERT_ARGB, &geo->colors[4*inst->minVert], src, + inst->numVertices, inst->stride); + src += 4; + } + + for(int32 i = 0; i < geo->numTexCoordSets; i++){ + uninstV2d(VERT_FLOAT2, &geo->texCoords[i][2*inst->minVert], src, + inst->numVertices, inst->stride); + src += 8; + } + unlockVertices(inst->vertexBuffer); +} + +ObjPipeline* +makeDefaultPipeline(void) +{ + ObjPipeline *pipe = new ObjPipeline(PLATFORM_D3D8); + pipe->instanceCB = defaultInstanceCB; + pipe->uninstanceCB = defaultUninstanceCB; + pipe->renderCB = defaultRenderCB; + return pipe; +} + +ObjPipeline* +makeSkinPipeline(void) +{ + ObjPipeline *pipe = new ObjPipeline(PLATFORM_D3D8); + pipe->instanceCB = defaultInstanceCB; + pipe->uninstanceCB = defaultUninstanceCB; + pipe->renderCB = defaultRenderCB; + pipe->pluginID = ID_SKIN; + pipe->pluginData = 1; + return pipe; +} + +ObjPipeline* +makeMatFXPipeline(void) +{ + ObjPipeline *pipe = new ObjPipeline(PLATFORM_D3D8); + pipe->instanceCB = defaultInstanceCB; + pipe->uninstanceCB = defaultUninstanceCB; + pipe->renderCB = defaultRenderCB; + pipe->pluginID = ID_MATFX; + pipe->pluginData = 0; + 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]; + uint8 *data; + + Image *img = Image::create(width, height, 32); + img->allocate(); + + if(format & Raster::PAL4) + stream->read(palette, 4*32); + else if(format & Raster::PAL8) + stream->read(palette, 4*256); + + // Only read one mipmap + for(int32 i = 0; i < numLevels; i++){ + uint32 size = stream->readU32(); + if(i == 0){ + data = new uint8[size]; + stream->read(data, size); + }else + stream->seek(size); + } + + 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]; + line[3] = palette[*idx*4+3]; + line += 4; + idx++; + } + pixels += img->stride; + } + } + + delete[] data; + Raster *ras = Raster::createFromImage(img); + 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->read(tex->name, 32); + stream->read(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); + tex->streamReadPlugins(stream); + return tex; + } + } + + Raster *raster; + D3dRaster *ras; + if(compression){ + raster = Raster::create(width, height, depth, format | type | 0x80, PLATFORM_D3D8); + ras = PLUGINOFFSET(D3dRaster, raster, nativeRasterOffset); + allocateDXT(raster, compression, numLevels, hasAlpha); + ras->customFormat = 1; + }else{ + raster = Raster::create(width, height, depth, format | type, PLATFORM_D3D8); + ras = PLUGINOFFSET(D3dRaster, raster, nativeRasterOffset); + } + tex->raster = raster; + + // TODO: check if format supported and convert if necessary + + if(pallength != 0) + stream->read(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); + stream->read(data, size); + raster->unlock(i); + }else + stream->seek(size); + } + tex->streamReadPlugins(stream); + return tex; +} + +void +writeNativeTexture(Texture *tex, Stream *stream) +{ + int32 chunksize = getSizeNativeTexture(tex); + int32 plgsize = tex->streamGetPluginSize(); + writeChunkHeader(stream, ID_TEXTURENATIVE, chunksize); + writeChunkHeader(stream, ID_STRUCT, chunksize-24-plgsize); + stream->writeU32(PLATFORM_D3D8); + + // Texture + stream->writeU32(tex->filterAddressing); + stream->write(tex->name, 32); + stream->write(tex->mask, 32); + + // Raster + Raster *raster = tex->raster; + D3dRaster *ras = PLUGINOFFSET(D3dRaster, raster, nativeRasterOffset); + 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->write(ras->palette, 4*32); + else if(raster->format & Raster::PAL8) + stream->write(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); + stream->write(data, size); + raster->unlock(i); + } + tex->streamWritePlugins(stream); +} + +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); + size += 12 + tex->streamGetPluginSize(); + return size; +} + +} +} diff --git a/src/d3d/d3d8render.cpp b/src/d3d/d3d8render.cpp new file mode 100644 index 0000000..53d9627 --- /dev/null +++ b/src/d3d/d3d8render.cpp @@ -0,0 +1,51 @@ +#include +#include +#include +#include + +#include "../rwbase.h" +#include "../rwplg.h" +#include "../rwpipeline.h" +#include "../rwobjects.h" +#include "rwd3d.h" +#include "rwd3d8.h" + +namespace rw { +namespace d3d8 { +using namespace d3d; + +#ifndef RW_D3D9 +void defaultRenderCB(Atomic*, InstanceDataHeader*) {} +#else + +void +defaultRenderCB(Atomic *atomic, InstanceDataHeader *header) +{ + Geometry *geo = atomic->geometry; + Frame *f = atomic->getFrame(); + device->SetTransform(D3DTS_WORLD, (D3DMATRIX*)f->getLTM()); + + InstanceData *inst = header->inst; + for(uint32 i = 0; i < header->numMeshes; i++){ + d3d::setTexture(0, inst->material->texture); + d3d::setMaterial(inst->material); + d3d::setRenderState(D3DRS_AMBIENT, D3DCOLOR_ARGB(0xFF, 0x40, 0x40, 0x40)); + d3d::setRenderState(D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_MATERIAL); + d3d::setRenderState(D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_MATERIAL); + if(geo->geoflags & Geometry::PRELIT) + d3d::setRenderState(D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_COLOR1); + + device->SetFVF(inst->vertexShader); + device->SetStreamSource(0, (IDirect3DVertexBuffer9*)inst->vertexBuffer, 0, inst->stride); + device->SetIndices((IDirect3DIndexBuffer9*)inst->indexBuffer); + uint32 numPrim = inst->primType == D3DPT_TRIANGLESTRIP ? inst->numIndices-2 : inst->numIndices/3; + d3d::flushCache(); + device->DrawIndexedPrimitive((D3DPRIMITIVETYPE)inst->primType, inst->baseIndex, + 0, inst->numVertices, 0, numPrim); + inst++; + } +} + +#endif +} +} diff --git a/src/d3d/d3d9.cpp b/src/d3d/d3d9.cpp new file mode 100644 index 0000000..dc14002 --- /dev/null +++ b/src/d3d/d3d9.cpp @@ -0,0 +1,727 @@ +#include +#include +#include +#include + +#include "../rwbase.h" +#include "../rwerror.h" +#include "../rwplg.h" +#include "../rwpipeline.h" +#include "../rwobjects.h" +#include "../rwengine.h" +#include "rwd3d.h" +#include "rwd3d9.h" + +#define PLUGIN_ID 2 + +namespace rw { +namespace d3d9 { +using namespace d3d; + +// TODO: move to header, but not as #define +#ifndef RW_D3D9 +#define D3DDECL_END() {0xFF,0,D3DDECLTYPE_UNUSED,0,0,0} +#endif + +#define NUMDECLELT 12 + +void +initializePlatform(void) +{ + driver[PLATFORM_D3D9].defaultPipeline = makeDefaultPipeline(); +} + +void* +createVertexDeclaration(VertexElement *elements) +{ +#ifdef RW_D3D9 + IDirect3DVertexDeclaration9 *decl = 0; + device->CreateVertexDeclaration((D3DVERTEXELEMENT9*)elements, &decl); + return decl; +#else + int n = 0; + VertexElement *e = (VertexElement*)elements; + while(e[n++].stream != 0xFF) + ; + e = (VertexElement*)new uint8[n*sizeof(VertexElement)]; + memcpy(e, elements, n*sizeof(VertexElement)); + return e; +#endif +} + +uint32 +getDeclaration(void *declaration, VertexElement *elements) +{ +#ifdef RW_D3D9 + IDirect3DVertexDeclaration9 *decl = (IDirect3DVertexDeclaration9*)declaration; + UINT numElt; + decl->GetDeclaration((D3DVERTEXELEMENT9*)elements, &numElt); + return numElt; +#else + int n = 0; + VertexElement *e = (VertexElement*)declaration; + while(e[n++].stream != 0xFF) + ; + if(elements) + memcpy(elements, declaration, n*sizeof(VertexElement)); + return n; +#endif +} + + +void* +destroyNativeData(void *object, int32, int32) +{ + Geometry *geometry = (Geometry*)object; + if(geometry->instData == nil || + geometry->instData->platform != PLATFORM_D3D9) + return object; + InstanceDataHeader *header = + (InstanceDataHeader*)geometry->instData; + geometry->instData = nil; + deleteObject(header->vertexDeclaration); + deleteObject(header->indexBuffer); + deleteObject(header->vertexStream[0].vertexBuffer); + deleteObject(header->vertexStream[1].vertexBuffer); + delete[] header->inst; + delete header; + return object; +} + +Stream* +readNativeData(Stream *stream, int32, void *object, int32, int32) +{ + 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_D3D9){ + RWERROR((ERR_PLATFORM, platform)); + return nil; + } + InstanceDataHeader *header = new InstanceDataHeader; + geometry->instData = header; + header->platform = PLATFORM_D3D9; + + int32 size = stream->readI32(); + uint8 *data = new uint8[size]; + stream->read(data, size); + uint8 *p = data; + header->serialNumber = *(uint32*)p; p += 4; + header->numMeshes = *(uint32*)p; p += 4; + header->indexBuffer = nil; p += 4; + header->primType = *(uint32*)p; p += 4; + p += 16*2; // skip vertex streams, they're repeated with the vertex buffers + header->useOffsets = *(bool32*)p; p += 4; + header->vertexDeclaration = nil; p += 4; + header->totalNumIndex = *(uint32*)p; p += 4; + header->totalNumVertex = *(uint32*)p; p += 4; + header->inst = new InstanceData[header->numMeshes]; + + InstanceData *inst = header->inst; + for(uint32 i = 0; i < header->numMeshes; i++){ + inst->numIndex = *(uint32*)p; p += 4; + inst->minVert = *(uint32*)p; p += 4; + uint32 matid = *(uint32*)p; p += 4; + inst->material = geometry->materialList[matid]; + inst->vertexAlpha = *(bool32*)p; p += 4; + inst->vertexShader = nil; p += 4; + inst->baseIndex = 0; p += 4; + inst->numVertices = *(uint32*)p; p += 4; + inst->startIndex = *(uint32*)p; p += 4; + inst->numPrimitives = *(uint32*)p; p += 4; + inst++; + } + + VertexElement elements[NUMDECLELT]; + uint32 numDeclarations = stream->readU32(); + stream->read(elements, numDeclarations*8); + header->vertexDeclaration = createVertexDeclaration(elements); + + header->indexBuffer = createIndexBuffer(header->totalNumIndex*2); + uint16 *indices = lockIndices(header->indexBuffer, 0, 0, 0); + stream->read(indices, 2*header->totalNumIndex); + unlockIndices(header->indexBuffer); + + VertexStream *s; + p = data; + for(int i = 0; i < 2; i++){ + stream->read(p, 16); + s = &header->vertexStream[i]; + s->vertexBuffer = (void*)*(uint32*)p; p += 4; + s->offset = 0; p += 4; + s->stride = *(uint32*)p; p += 4; + s->geometryFlags = *(uint16*)p; p += 2; + s->managed = *p++; + s->dynamicLock = *p++; + + if(s->vertexBuffer == nil) + continue; + // TODO: unset managed flag when using morph targets. + // also uses different buffer type and locks differently + s->vertexBuffer = createVertexBuffer(s->stride*header->totalNumVertex, 0, D3DPOOL_MANAGED); + uint8 *verts = lockVertices(s->vertexBuffer, 0, 0, D3DLOCK_NOSYSLOCK); + stream->read(verts, s->stride*header->totalNumVertex); + unlockVertices(s->vertexBuffer); + } + + // TODO: somehow depends on number of streams used (baseIndex = minVert when more than one) + inst = header->inst; + for(uint32 i = 0; i < header->numMeshes; i++){ + inst->baseIndex = inst->minVert + header->vertexStream[0].offset / header->vertexStream[0].stride; + inst++; + } + + delete[] data; + return stream; +} + +Stream* +writeNativeData(Stream *stream, int32 len, void *object, int32, int32) +{ + Geometry *geometry = (Geometry*)object; + writeChunkHeader(stream, ID_STRUCT, len-12); + if(geometry->instData == nil || + geometry->instData->platform != PLATFORM_D3D9) + return stream; + stream->writeU32(PLATFORM_D3D9); + InstanceDataHeader *header = (InstanceDataHeader*)geometry->instData; + int32 size = 64 + geometry->meshHeader->numMeshes*36; + uint8 *data = new uint8[size]; + stream->writeI32(size); + + uint8 *p = data; + *(uint32*)p = header->serialNumber; p += 4; + *(uint32*)p = header->numMeshes; p += 4; + p += 4; // skip index buffer + *(uint32*)p = header->primType; p += 4; + p += 16*2; // skip vertex streams, they're repeated with the vertex buffers + *(bool32*)p = header->useOffsets; p += 4; + p += 4; // skip vertex declaration + *(uint32*)p = header->totalNumIndex; p += 4; + *(uint32*)p = header->totalNumVertex; p += 4; + + InstanceData *inst = header->inst; + for(uint32 i = 0; i < header->numMeshes; i++){ + *(uint32*)p = inst->numIndex; p += 4; + *(uint32*)p = inst->minVert; p += 4; + int32 matid = findPointer(inst->material, (void**)geometry->materialList, geometry->numMaterials); + *(int32*)p = matid; p += 4; + *(bool32*)p = inst->vertexAlpha; p += 4; + *(uint32*)p = 0; p += 4; // vertex shader + *(uint32*)p = inst->baseIndex; p += 4; // not used but meh... + *(uint32*)p = inst->numVertices; p += 4; + *(uint32*)p = inst->startIndex; p += 4; + *(uint32*)p = inst->numPrimitives; p += 4; + inst++; + } + stream->write(data, size); + + VertexElement elements[NUMDECLELT]; + uint32 numElt = getDeclaration(header->vertexDeclaration, elements); + stream->writeU32(numElt); + stream->write(elements, 8*numElt); + + uint16 *indices = lockIndices(header->indexBuffer, 0, 0, 0); + stream->write(indices, 2*header->totalNumIndex); + unlockIndices(header->indexBuffer); + + VertexStream *s; + for(int i = 0; i < 2; i++){ + s = &header->vertexStream[i]; + p = data; + *(uint32*)p = s->vertexBuffer ? 0xbadeaffe : 0; p += 4; + *(uint32*)p = s->offset; p += 4; + *(uint32*)p = s->stride; p += 4; + *(uint16*)p = s->geometryFlags; p += 2; + *p++ = s->managed; + *p++ = s->dynamicLock; + stream->write(data, 16); + + if(s->vertexBuffer == nil) + continue; + uint8 *verts = lockVertices(s->vertexBuffer, 0, 0, D3DLOCK_NOSYSLOCK); + stream->write(verts, s->stride*header->totalNumVertex); + unlockVertices(s->vertexBuffer); + } + + delete[] data; + return stream; +} + +int32 +getSizeNativeData(void *object, int32, int32) +{ + Geometry *geometry = (Geometry*)object; + if(geometry->instData == nil || + geometry->instData->platform != PLATFORM_D3D9) + return 0; + InstanceDataHeader *header = (InstanceDataHeader*)geometry->instData; + int32 size = 12 + 4 + 4 + 64 + header->numMeshes*36; + uint32 numElt = getDeclaration(header->vertexDeclaration, nil); + size += 4 + numElt*8; + size += 2*header->totalNumIndex; + size += 0x10 + header->vertexStream[0].stride*header->totalNumVertex; + size += 0x10 + header->vertexStream[1].stride*header->totalNumVertex; + 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; + if(geo->geoflags & Geometry::NATIVE) + return; + geo->geoflags |= Geometry::NATIVE; + InstanceDataHeader *header = new InstanceDataHeader; + MeshHeader *meshh = geo->meshHeader; + geo->instData = header; + header->platform = PLATFORM_D3D9; + + header->serialNumber = 0; + header->numMeshes = meshh->numMeshes; + header->primType = meshh->flags == 1 ? D3DPT_TRIANGLESTRIP : D3DPT_TRIANGLELIST; + header->useOffsets = 0; + header->totalNumVertex = geo->numVertices; + header->totalNumIndex = meshh->totalIndices; + header->inst = new InstanceData[header->numMeshes]; + + header->indexBuffer = createIndexBuffer(header->totalNumIndex*2); + + uint16 *indices = lockIndices(header->indexBuffer, 0, 0, 0); + InstanceData *inst = header->inst; + Mesh *mesh = meshh->mesh; + uint32 startindex = 0; + for(uint32 i = 0; i < header->numMeshes; i++){ + findMinVertAndNumVertices(mesh->indices, mesh->numIndices, + &inst->minVert, (int32*)&inst->numVertices); + inst->numIndex = mesh->numIndices; + inst->material = mesh->material; + inst->vertexAlpha = 0; + inst->vertexShader = nil; + inst->baseIndex = inst->minVert; + inst->startIndex = startindex; + inst->numPrimitives = header->primType == D3DPT_TRIANGLESTRIP ? inst->numIndex-2 : inst->numIndex/3; + if(inst->minVert == 0) + memcpy(&indices[inst->startIndex], mesh->indices, inst->numIndex*2); + else + for(uint32 j = 0; j < inst->numIndex; j++) + indices[inst->startIndex+j] = mesh->indices[j] - inst->minVert; + startindex += inst->numIndex; + mesh++; + inst++; + } + unlockIndices(header->indexBuffer); + + memset(&header->vertexStream, 0, 2*sizeof(VertexStream)); + + pipe->instanceCB(geo, header); +} + +static void +uninstance(rw::ObjPipeline *rwpipe, Atomic *atomic) +{ + ObjPipeline *pipe = (ObjPipeline*)rwpipe; + Geometry *geo = atomic->geometry; + if((geo->geoflags & Geometry::NATIVE) == 0) + return; + assert(geo->instData != nil); + assert(geo->instData->platform == PLATFORM_D3D9); + geo->geoflags &= ~Geometry::NATIVE; + geo->allocateData(); + geo->meshHeader->allocateIndices(); + + InstanceDataHeader *header = (InstanceDataHeader*)geo->instData; + uint16 *indices = lockIndices(header->indexBuffer, 0, 0, 0); + InstanceData *inst = header->inst; + Mesh *mesh = geo->meshHeader->mesh; + for(uint32 i = 0; i < header->numMeshes; i++){ + if(inst->minVert == 0) + memcpy(mesh->indices, &indices[inst->startIndex], inst->numIndex*2); + else + for(uint32 j = 0; j < inst->numIndex; j++) + mesh->indices[j] = indices[inst->startIndex+j] + inst->minVert; + mesh++; + inst++; + } + unlockIndices(header->indexBuffer); + + pipe->uninstanceCB(geo, header); + geo->generateTriangles(); + destroyNativeData(geo, 0, 0); +} + +static void +render(rw::ObjPipeline *rwpipe, Atomic *atomic) +{ + ObjPipeline *pipe = (ObjPipeline*)rwpipe; + Geometry *geo = atomic->geometry; + if((geo->geoflags & Geometry::NATIVE) == 0) + pipe->instance(atomic); + assert(geo->instData != nil); + assert(geo->instData->platform == PLATFORM_D3D9); + if(pipe->renderCB) + pipe->renderCB(atomic, (InstanceDataHeader*)geo->instData); +} + +ObjPipeline::ObjPipeline(uint32 platform) + : rw::ObjPipeline(platform) +{ + this->impl.instance = d3d9::instance; + this->impl.uninstance = d3d9::uninstance; + this->impl.render = d3d9::render; + this->instanceCB = nil; + this->uninstanceCB = nil; + this->renderCB = nil; +} + +void +defaultInstanceCB(Geometry *geo, InstanceDataHeader *header) +{ + VertexElement dcl[NUMDECLELT]; + + VertexStream *s = &header->vertexStream[0]; + s->offset = 0; + s->managed = 1; + s->geometryFlags = 0; + s->dynamicLock = 0; + + int i = 0; + dcl[i].stream = 0; + dcl[i].offset = 0; + dcl[i].type = D3DDECLTYPE_FLOAT3; + dcl[i].method = D3DDECLMETHOD_DEFAULT; + dcl[i].usage = D3DDECLUSAGE_POSITION; + dcl[i].usageIndex = 0; + i++; + uint16 stride = 12; + s->geometryFlags |= 0x2; + + bool isPrelit = (geo->geoflags & Geometry::PRELIT) != 0; + if(isPrelit){ + dcl[i].stream = 0; + dcl[i].offset = stride; + dcl[i].type = D3DDECLTYPE_D3DCOLOR; + dcl[i].method = D3DDECLMETHOD_DEFAULT; + dcl[i].usage = D3DDECLUSAGE_COLOR; + dcl[i].usageIndex = 0; + i++; + s->geometryFlags |= 0x8; + stride += 4; + } + + for(int32 n = 0; n < geo->numTexCoordSets; n++){ + dcl[i].stream = 0; + dcl[i].offset = stride; + dcl[i].type = D3DDECLTYPE_FLOAT2; + dcl[i].method = D3DDECLMETHOD_DEFAULT; + dcl[i].usage = D3DDECLUSAGE_TEXCOORD; + dcl[i].usageIndex = (uint8)n; + i++; + s->geometryFlags |= 0x10 << n; + stride += 8; + } + + bool hasNormals = (geo->geoflags & Geometry::NORMALS) != 0; + if(hasNormals){ + dcl[i].stream = 0; + dcl[i].offset = stride; + dcl[i].type = D3DDECLTYPE_FLOAT3; + dcl[i].method = D3DDECLMETHOD_DEFAULT; + dcl[i].usage = D3DDECLUSAGE_NORMAL; + dcl[i].usageIndex = 0; + i++; + s->geometryFlags |= 0x4; + stride += 12; + } + dcl[i] = D3DDECL_END(); + header->vertexStream[0].stride = stride; + + header->vertexDeclaration = createVertexDeclaration((VertexElement*)dcl); + + s->vertexBuffer = createVertexBuffer(header->totalNumVertex*s->stride, 0, D3DPOOL_MANAGED); + + // TODO: support both vertex buffers + uint8 *verts = lockVertices(s->vertexBuffer, 0, 0, D3DLOCK_NOSYSLOCK); + for(i = 0; dcl[i].usage != D3DDECLUSAGE_POSITION || dcl[i].usageIndex != 0; i++) + ; + instV3d(vertFormatMap[dcl[i].type], verts + dcl[i].offset, + geo->morphTargets[0].vertices, + header->totalNumVertex, + header->vertexStream[dcl[i].stream].stride); + + if(isPrelit){ + for(i = 0; dcl[i].usage != D3DDECLUSAGE_COLOR || dcl[i].usageIndex != 0; i++) + ; + // TODO: vertex alpha (instance per mesh) + instColor(vertFormatMap[dcl[i].type], verts + dcl[i].offset, + geo->colors, + header->totalNumVertex, + header->vertexStream[dcl[i].stream].stride); + } + + for(int32 n = 0; n < geo->numTexCoordSets; n++){ + for(i = 0; dcl[i].usage != D3DDECLUSAGE_TEXCOORD || dcl[i].usageIndex != n; i++) + ; + instV2d(vertFormatMap[dcl[i].type], verts + dcl[i].offset, + geo->texCoords[n], + header->totalNumVertex, + header->vertexStream[dcl[i].stream].stride); + } + + if(hasNormals){ + for(i = 0; dcl[i].usage != D3DDECLUSAGE_NORMAL || dcl[i].usageIndex != 0; i++) + ; + instV3d(vertFormatMap[dcl[i].type], verts + dcl[i].offset, + geo->morphTargets[0].normals, + header->totalNumVertex, + header->vertexStream[dcl[i].stream].stride); + } + unlockVertices(s->vertexBuffer); +} + +void +defaultUninstanceCB(Geometry *geo, InstanceDataHeader *header) +{ + VertexElement dcl[NUMDECLELT]; + + uint8 *verts[2]; + verts[0] = lockVertices(header->vertexStream[0].vertexBuffer, 0, 0, D3DLOCK_NOSYSLOCK); + verts[1] = lockVertices(header->vertexStream[1].vertexBuffer, 0, 0, D3DLOCK_NOSYSLOCK); + + int i; + for(i = 0; dcl[i].usage != D3DDECLUSAGE_POSITION || dcl[i].usageIndex != 0; i++) + ; + uninstV3d(vertFormatMap[dcl[i].type], + geo->morphTargets[0].vertices, + verts[dcl[i].stream] + dcl[i].offset, + header->totalNumVertex, + header->vertexStream[dcl[i].stream].stride); + + if(geo->geoflags & Geometry::PRELIT){ + for(i = 0; dcl[i].usage != D3DDECLUSAGE_COLOR || dcl[i].usageIndex != 0; i++) + ; + uninstColor(vertFormatMap[dcl[i].type], + geo->colors, + verts[dcl[i].stream] + dcl[i].offset, + header->totalNumVertex, + header->vertexStream[dcl[i].stream].stride); + } + + for(int32 n = 0; n < geo->numTexCoordSets; n++){ + for(i = 0; dcl[i].usage != D3DDECLUSAGE_TEXCOORD || dcl[i].usageIndex != n; i++) + ; + uninstV2d(vertFormatMap[dcl[i].type], + geo->texCoords[n], + verts[dcl[i].stream] + dcl[i].offset, + header->totalNumVertex, + header->vertexStream[dcl[i].stream].stride); + } + + if(geo->geoflags & Geometry::NORMALS){ + for(i = 0; dcl[i].usage != D3DDECLUSAGE_NORMAL || dcl[i].usageIndex != 0; i++) + ; + uninstV3d(vertFormatMap[dcl[i].type], + geo->morphTargets[0].normals, + verts[dcl[i].stream] + dcl[i].offset, + header->totalNumVertex, + header->vertexStream[dcl[i].stream].stride); + } + + unlockVertices(verts[0]); + unlockVertices(verts[1]); +} + +ObjPipeline* +makeDefaultPipeline(void) +{ + ObjPipeline *pipe = new ObjPipeline(PLATFORM_D3D9); + pipe->instanceCB = defaultInstanceCB; + pipe->uninstanceCB = defaultUninstanceCB; + pipe->renderCB = defaultRenderCB; + return pipe; +} + +ObjPipeline* +makeSkinPipeline(void) +{ + ObjPipeline *pipe = new ObjPipeline(PLATFORM_D3D9); + pipe->instanceCB = defaultInstanceCB; + pipe->uninstanceCB = defaultUninstanceCB; + pipe->renderCB = defaultRenderCB; + pipe->pluginID = ID_SKIN; + pipe->pluginData = 1; + return pipe; +} + +ObjPipeline* +makeMatFXPipeline(void) +{ + ObjPipeline *pipe = new ObjPipeline(PLATFORM_D3D9); + pipe->instanceCB = defaultInstanceCB; + pipe->uninstanceCB = defaultUninstanceCB; + pipe->renderCB = defaultRenderCB; + pipe->pluginID = ID_MATFX; + pipe->pluginData = 0; + return pipe; +} + +// Native Texture and Raster + +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_D3D9){ + RWERROR((ERR_PLATFORM, platform)); + return nil; + } + Texture *tex = Texture::create(nil); + if(tex == nil) + return nil; + + // Texture + tex->filterAddressing = stream->readU32(); + stream->read(tex->name, 32); + stream->read(tex->mask, 32); + + // Raster + int32 format = stream->readI32(); + int32 d3dformat = stream->readI32(); + int32 width = stream->readU16(); + int32 height = stream->readU16(); + int32 depth = stream->readU8(); + int32 numLevels = stream->readU8(); + int32 type = stream->readU8(); + int32 flags = stream->readU8(); + + Raster *raster; + D3dRaster *ras; + + assert((flags & 2) == 0); + if(flags & 8){ + raster = Raster::create(width, height, depth, format | type | 0x80, PLATFORM_D3D9); + ras = PLUGINOFFSET(D3dRaster, raster, nativeRasterOffset); + ras->format = d3dformat; + ras->hasAlpha = flags & 1; + ras->texture = createTexture(raster->width, raster->height, + raster->format & Raster::MIPMAP ? numLevels : 1, + ras->format); + raster->flags &= ~0x80; + ras->customFormat = 1; + }else{ + raster = Raster::create(width, height, depth, format | type, PLATFORM_D3D9); + ras = PLUGINOFFSET(D3dRaster, raster, nativeRasterOffset); + } + tex->raster = raster; + + // TODO: check if format supported and convert if necessary + + if(raster->format & Raster::PAL4) + stream->read(ras->palette, 4*32); + else if(raster->format & Raster::PAL8) + stream->read(ras->palette, 4*256); + + uint32 size; + uint8 *data; + for(int32 i = 0; i < numLevels; i++){ + size = stream->readU32(); + if(i < raster->getNumLevels()){ + data = raster->lock(i); + stream->read(data, size); + raster->unlock(i); + }else + stream->seek(size); + } + tex->streamReadPlugins(stream); + return tex; +} + +void +writeNativeTexture(Texture *tex, Stream *stream) +{ + int32 chunksize = getSizeNativeTexture(tex); + int32 plgsize = tex->streamGetPluginSize(); + writeChunkHeader(stream, ID_TEXTURENATIVE, chunksize); + writeChunkHeader(stream, ID_STRUCT, chunksize-24-plgsize); + stream->writeU32(PLATFORM_D3D9); + + // Texture + stream->writeU32(tex->filterAddressing); + stream->write(tex->name, 32); + stream->write(tex->mask, 32); + + // Raster + Raster *raster = tex->raster; + D3dRaster *ras = PLUGINOFFSET(D3dRaster, raster, nativeRasterOffset); + int32 numLevels = raster->getNumLevels(); + stream->writeI32(raster->format); + stream->writeU32(ras->format); + stream->writeU16(raster->width); + stream->writeU16(raster->height); + stream->writeU8(raster->depth); + stream->writeU8(numLevels); + stream->writeU8(raster->type); + uint8 flags = 0; + if(ras->hasAlpha) + flags |= 1; + // 2 - cube map + // 4 - something about mipmaps... + if(ras->customFormat) + flags |= 8; + stream->writeU8(flags); + + if(raster->format & Raster::PAL4) + stream->write(ras->palette, 4*32); + else if(raster->format & Raster::PAL8) + stream->write(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); + stream->write(data, size); + raster->unlock(i); + } + tex->streamWritePlugins(stream); +} + +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); + size += 12 + tex->streamGetPluginSize(); + return size; +} + +} +} diff --git a/src/d3d/d3d9render.cpp b/src/d3d/d3d9render.cpp new file mode 100644 index 0000000..0a3ab26 --- /dev/null +++ b/src/d3d/d3d9render.cpp @@ -0,0 +1,52 @@ +#include +#include +#include +#include + +#include "../rwbase.h" +#include "../rwplg.h" +#include "../rwpipeline.h" +#include "../rwobjects.h" +#include "rwd3d.h" +#include "rwd3d9.h" + +namespace rw { +namespace d3d9 { +using namespace d3d; + +#ifndef RW_D3D9 +void defaultRenderCB(Atomic*, InstanceDataHeader*) {} +#else + +void +defaultRenderCB(Atomic *atomic, InstanceDataHeader *header) +{ + Geometry *geo = atomic->geometry; + Frame *f = atomic->getFrame(); + device->SetTransform(D3DTS_WORLD, (D3DMATRIX*)f->getLTM()); + + device->SetStreamSource(0, (IDirect3DVertexBuffer9*)header->vertexStream[0].vertexBuffer, + 0, header->vertexStream[0].stride); + device->SetIndices((IDirect3DIndexBuffer9*)header->indexBuffer); + device->SetVertexDeclaration((IDirect3DVertexDeclaration9*)header->vertexDeclaration); + + InstanceData *inst = header->inst; + for(uint32 i = 0; i < header->numMeshes; i++){ + d3d::setTexture(0, inst->material->texture); + d3d::setMaterial(inst->material); + d3d::setRenderState(D3DRS_AMBIENT, D3DCOLOR_ARGB(0xFF, 0x40, 0x40, 0x40)); + d3d::setRenderState(D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_MATERIAL); + d3d::setRenderState(D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_MATERIAL); + if(geo->geoflags & Geometry::PRELIT) + d3d::setRenderState(D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_COLOR1); + d3d::flushCache(); + device->DrawIndexedPrimitive((D3DPRIMITIVETYPE)header->primType, inst->baseIndex, + 0, inst->numVertices, + inst->startIndex, inst->numPrimitives); + inst++; + } +} + +#endif +} +} diff --git a/src/d3d/d3ddriver.cpp b/src/d3d/d3ddriver.cpp new file mode 100644 index 0000000..37ea7c6 --- /dev/null +++ b/src/d3d/d3ddriver.cpp @@ -0,0 +1,182 @@ +#include +#include +#include +#include + +#include "../rwbase.h" +#include "../rwplg.h" +#include "../rwpipeline.h" +#include "../rwobjects.h" +#include "rwd3d.h" + +namespace rw { +namespace d3d { + +#ifdef RW_D3D9 + +#define MAXNUMSTATES D3DRS_BLENDOPALPHA +#define MAXNUMSTAGES 8 +#define MAXNUMTEXSTATES D3DTSS_CONSTANT +#define MAXNUMSAMPLERSTATES D3DSAMP_DMAPOFFSET + +static int32 numDirtyStates; +static uint32 dirtyStates[MAXNUMSTATES]; +static struct { + uint32 value; + bool32 dirty; +} stateCache[MAXNUMSTATES]; +static uint32 d3dStates[MAXNUMSTATES]; + +static int32 numDirtyTextureStageStates; +static struct { + uint32 stage; + uint32 type; +} dirtyTextureStageStates[MAXNUMTEXSTATES*MAXNUMSTAGES]; +static struct { + uint32 value; + bool32 dirty; +} textureStageStateCache[MAXNUMSTATES][MAXNUMSTAGES]; +static uint32 d3dTextureStageStates[MAXNUMSTATES][MAXNUMSTAGES]; + +static uint32 d3dSamplerStates[MAXNUMSAMPLERSTATES][MAXNUMSTAGES]; + +static Raster *d3dRaster[MAXNUMSTAGES]; + +static D3DMATERIAL9 d3dmaterial; + +void +setRenderState(uint32 state, uint32 value) +{ + if(stateCache[state].value != value){ + stateCache[state].value = value; + if(!stateCache[state].dirty){ + stateCache[state].dirty = 1; + dirtyStates[numDirtyStates++] = state; + } + } +} + +void +setTextureStageState(uint32 stage, uint32 type, uint32 value) +{ + if(textureStageStateCache[type][stage].value != value){ + textureStageStateCache[type][stage].value = value; + if(!textureStageStateCache[type][stage].dirty){ + textureStageStateCache[type][stage].dirty = 1; + dirtyTextureStageStates[numDirtyTextureStageStates].stage = stage; + dirtyTextureStageStates[numDirtyTextureStageStates].type = type; + numDirtyTextureStageStates++; + } + } +} + +void +flushCache(void) +{ + uint32 s, t; + uint32 v; + for(int32 i = 0; i < numDirtyStates; i++){ + s = dirtyStates[i]; + v = stateCache[s].value; + stateCache[s].dirty = 0; + if(d3dStates[s] != v){ + device->SetRenderState((D3DRENDERSTATETYPE)s, v); + d3dStates[s] = v; + } + } + numDirtyStates = 0; + for(int32 i = 0; i < numDirtyTextureStageStates; i++){ + s = dirtyTextureStageStates[i].stage; + t = dirtyTextureStageStates[i].type; + v = textureStageStateCache[t][s].value; + textureStageStateCache[t][s].dirty = 0; + if(d3dTextureStageStates[t][s] != v){ + device->SetTextureStageState(s, (D3DTEXTURESTAGESTATETYPE)t, v); + d3dTextureStageStates[t][s] = v; + } + } + numDirtyTextureStageStates = 0; +} + +void +setSamplerState(uint32 stage, uint32 type, uint32 value) +{ + if(d3dSamplerStates[type][stage] != value){ + device->SetSamplerState(stage, (D3DSAMPLERSTATETYPE)type, value); + d3dSamplerStates[type][stage] = value; + } +} + +void +setRasterStage(uint32 stage, Raster *raster) +{ + D3dRaster *d3draster = nil; + if(raster != d3dRaster[stage]){ + d3dRaster[stage] = raster; + if(raster){ + d3draster = PLUGINOFFSET(D3dRaster, raster, nativeRasterOffset); + device->SetTexture(stage, (IDirect3DTexture9*)d3draster->texture); + }else + device->SetTexture(stage, nil); + } +} + +void +setTexture(uint32 stage, Texture *tex) +{ + static DWORD filternomip[] = { + 0, D3DTEXF_POINT, D3DTEXF_LINEAR, + D3DTEXF_POINT, D3DTEXF_LINEAR, + D3DTEXF_POINT, D3DTEXF_LINEAR + }; + static DWORD wrap[] = { + 0, D3DTADDRESS_WRAP, D3DTADDRESS_MIRROR, + D3DTADDRESS_CLAMP, D3DTADDRESS_BORDER + }; + if(tex == nil){ + setRasterStage(stage, nil); + return; + } + if(tex->raster){ + setSamplerState(stage, D3DSAMP_MAGFILTER, filternomip[tex->filterAddressing & 0xFF]); + setSamplerState(stage, D3DSAMP_MINFILTER, filternomip[tex->filterAddressing & 0xFF]); + setSamplerState(stage, D3DSAMP_ADDRESSU, wrap[(tex->filterAddressing >> 8) & 0xF]); + setSamplerState(stage, D3DSAMP_ADDRESSV, wrap[(tex->filterAddressing >> 12) & 0xF]); + } + setRasterStage(stage, tex->raster); +} + +void +setMaterial(Material *mat) +{ + D3DMATERIAL9 mat9; + D3DCOLORVALUE black = { 0, 0, 0, 0 }; + float ambmult = mat->surfaceProps.ambient/255.0f; + float diffmult = mat->surfaceProps.diffuse/255.0f; + mat9.Ambient.r = mat->color.red*ambmult; + mat9.Ambient.g = mat->color.green*ambmult; + mat9.Ambient.b = mat->color.blue*ambmult; + mat9.Ambient.a = mat->color.alpha*ambmult; + mat9.Diffuse.r = mat->color.red*diffmult; + mat9.Diffuse.g = mat->color.green*diffmult; + mat9.Diffuse.b = mat->color.blue*diffmult; + mat9.Diffuse.a = mat->color.alpha*diffmult; + mat9.Power = 0.0f; + mat9.Emissive = black; + mat9.Specular = black; + if(d3dmaterial.Diffuse.r != mat9.Diffuse.r || + d3dmaterial.Diffuse.g != mat9.Diffuse.g || + d3dmaterial.Diffuse.b != mat9.Diffuse.b || + d3dmaterial.Diffuse.a != mat9.Diffuse.a || + d3dmaterial.Ambient.r != mat9.Ambient.r || + d3dmaterial.Ambient.g != mat9.Ambient.g || + d3dmaterial.Ambient.b != mat9.Ambient.b || + d3dmaterial.Ambient.a != mat9.Ambient.a){ + device->SetMaterial(&mat9); + d3dmaterial = mat9; + } +} + +#endif +} +} diff --git a/src/d3d/rwd3d.h b/src/d3d/rwd3d.h new file mode 100644 index 0000000..04ebde0 --- /dev/null +++ b/src/d3d/rwd3d.h @@ -0,0 +1,105 @@ +#ifdef RW_D3D9 +#include +#endif + +namespace rw { +namespace d3d { + +extern bool32 isP8supported; + +#ifdef RW_D3D9 +extern IDirect3DDevice9 *device; +#else +enum { + D3DLOCK_NOSYSLOCK = 0, // ignored + D3DPOOL_MANAGED = 0, // ignored + D3DPT_TRIANGLELIST = 4, + D3DPT_TRIANGLESTRIP = 5, + + + D3DDECLTYPE_FLOAT1 = 0, // 1D float expanded to (value, 0., 0., 1.) + D3DDECLTYPE_FLOAT2 = 1, // 2D float expanded to (value, value, 0., 1.) + D3DDECLTYPE_FLOAT3 = 2, // 3D float expanded to (value, value, value, 1.) + D3DDECLTYPE_FLOAT4 = 3, // 4D float + D3DDECLTYPE_D3DCOLOR = 4, // 4D packed unsigned bytes mapped to 0. to 1. range + // Input is in D3DCOLOR format (ARGB) expanded to (R, G, B, A) + D3DDECLTYPE_UBYTE4 = 5, // 4D unsigned byte + D3DDECLTYPE_SHORT2 = 6, // 2D signed short expanded to (value, value, 0., 1.) + D3DDECLTYPE_SHORT4 = 7, // 4D signed short + + D3DDECLTYPE_UBYTE4N = 8, // Each of 4 bytes is normalized by dividing to 255.0 + D3DDECLTYPE_SHORT2N = 9, // 2D signed short normalized (v[0]/32767.0,v[1]/32767.0,0,1) + D3DDECLTYPE_SHORT4N = 10, // 4D signed short normalized (v[0]/32767.0,v[1]/32767.0,v[2]/32767.0,v[3]/32767.0) + D3DDECLTYPE_USHORT2N = 11, // 2D unsigned short normalized (v[0]/65535.0,v[1]/65535.0,0,1) + D3DDECLTYPE_USHORT4N = 12, // 4D unsigned short normalized (v[0]/65535.0,v[1]/65535.0,v[2]/65535.0,v[3]/65535.0) + D3DDECLTYPE_UDEC3 = 13, // 3D unsigned 10 10 10 format expanded to (value, value, value, 1) + D3DDECLTYPE_DEC3N = 14, // 3D signed 10 10 10 format normalized and expanded to (v[0]/511.0, v[1]/511.0, v[2]/511.0, 1) + D3DDECLTYPE_FLOAT16_2 = 15, // Two 16-bit floating point values, expanded to (value, value, 0, 1) + D3DDECLTYPE_FLOAT16_4 = 16, // Four 16-bit floating point values + D3DDECLTYPE_UNUSED = 17, // When the type field in a decl is unused. + + + D3DDECLMETHOD_DEFAULT = 0, + + + D3DDECLUSAGE_POSITION = 0, + D3DDECLUSAGE_BLENDWEIGHT, // 1 + D3DDECLUSAGE_BLENDINDICES, // 2 + D3DDECLUSAGE_NORMAL, // 3 + D3DDECLUSAGE_PSIZE, // 4 + D3DDECLUSAGE_TEXCOORD, // 5 + D3DDECLUSAGE_TANGENT, // 6 + D3DDECLUSAGE_BINORMAL, // 7 + D3DDECLUSAGE_TESSFACTOR, // 8 + D3DDECLUSAGE_POSITIONT, // 9 + D3DDECLUSAGE_COLOR, // 10 + D3DDECLUSAGE_FOG, // 11 + D3DDECLUSAGE_DEPTH, // 12 + D3DDECLUSAGE_SAMPLE, // 13 +}; +#endif + +extern int vertFormatMap[]; + +void *createIndexBuffer(uint32 length); +uint16 *lockIndices(void *indexBuffer, uint32 offset, uint32 size, uint32 flags); +void unlockIndices(void *indexBuffer); +void *createVertexBuffer(uint32 length, uint32 fvf, int32 pool); +uint8 *lockVertices(void *vertexBuffer, uint32 offset, uint32 size, uint32 flags); +void unlockVertices(void *vertexBuffer); +void *createTexture(int32 width, int32 height, int32 levels, uint32 format); +uint8 *lockTexture(void *texture, int32 level); +void unlockTexture(void *texture, int32 level); +void deleteObject(void *object); + +// Native Texture and Raster + +struct D3dRaster +{ + void *texture; + void *palette; + uint32 format; + bool32 hasAlpha; + bool32 customFormat; +}; + +int32 getLevelSize(Raster *raster, int32 level); +void allocateDXT(Raster *raster, int32 dxt, int32 numLevels, bool32 hasAlpha); +void setPalette(Raster *raster, void *palette, int32 size); +void setTexels(Raster *raster, void *texels, int32 level); + +extern int32 nativeRasterOffset; +void registerNativeRaster(void); + +// Rendering + +void setRenderState(uint32 state, uint32 value); +void setTextureStageState(uint32 stage, uint32 type, uint32 value); +void flushCache(void); +void setSamplerState(uint32 stage, uint32 type, uint32 value); + +void setTexture(uint32 stage, Texture *tex); +void setMaterial(Material *mat); + +} +} diff --git a/src/d3d/rwd3d8.h b/src/d3d/rwd3d8.h new file mode 100644 index 0000000..d8aac01 --- /dev/null +++ b/src/d3d/rwd3d8.h @@ -0,0 +1,67 @@ +namespace rw { +namespace d3d8 { + +void initializePlatform(void); + +struct InstanceData +{ + uint32 minVert; + int32 stride; + int32 numVertices; + int32 numIndices; + Material *material; + uint32 vertexShader; + uint32 primType; + void *indexBuffer; + void *vertexBuffer; + uint32 baseIndex; + uint8 vertexAlpha; + uint8 managed; + uint8 remapped; +}; + +struct InstanceDataHeader : rw::InstanceDataHeader +{ + uint16 serialNumber; + uint16 numMeshes; + + InstanceData *inst; +}; + +uint32 makeFVFDeclaration(uint32 flags, int32 numTex); +int32 getStride(uint32 flags, int32 numTex); + +void *destroyNativeData(void *object, int32, int32); +Stream *readNativeData(Stream *stream, int32 len, void *object, int32, int32); +Stream *writeNativeData(Stream *stream, int32 len, void *object, int32, int32); +int32 getSizeNativeData(void *object, int32, int32); +void registerNativeDataPlugin(void); + +class ObjPipeline : public rw::ObjPipeline +{ +public: + void (*instanceCB)(Geometry *geo, InstanceData *header); + void (*uninstanceCB)(Geometry *geo, InstanceData *header); + void (*renderCB)(Atomic *atomic, InstanceDataHeader *header); + + ObjPipeline(uint32 platform); +}; + +void defaultInstanceCB(Geometry *geo, InstanceDataHeader *header); +void defaultUninstanceCB(Geometry *geo, InstanceDataHeader *header); +void defaultRenderCB(Atomic *atomic, InstanceDataHeader *header); + +ObjPipeline *makeDefaultPipeline(void); + +ObjPipeline *makeSkinPipeline(void); + +ObjPipeline *makeMatFXPipeline(void); + +// Native Texture and Raster + +Texture *readNativeTexture(Stream *stream); +void writeNativeTexture(Texture *tex, Stream *stream); +uint32 getSizeNativeTexture(Texture *tex); + +} +} diff --git a/src/d3d/rwd3d9.h b/src/d3d/rwd3d9.h new file mode 100644 index 0000000..a382965 --- /dev/null +++ b/src/d3d/rwd3d9.h @@ -0,0 +1,90 @@ +namespace rw { +namespace d3d9 { + +void initializePlatform(void); + +struct VertexElement +{ + uint16 stream; + uint16 offset; + uint8 type; + uint8 method; + uint8 usage; + uint8 usageIndex; +}; + +struct VertexStream +{ + void *vertexBuffer; + uint32 offset; + uint32 stride; + uint16 geometryFlags; + uint8 managed; + uint8 dynamicLock; +}; + +struct InstanceData +{ + uint32 numIndex; + uint32 minVert; + Material *material; + bool32 vertexAlpha; + void *vertexShader; + uint32 baseIndex; + uint32 numVertices; + uint32 startIndex; + uint32 numPrimitives; +}; + +struct InstanceDataHeader : rw::InstanceDataHeader +{ + uint32 serialNumber; + uint32 numMeshes; + void *indexBuffer; + uint32 primType; + VertexStream vertexStream[2]; + bool32 useOffsets; + void *vertexDeclaration; + uint32 totalNumIndex; + uint32 totalNumVertex; + + InstanceData *inst; +}; + +void *createVertexDeclaration(VertexElement *elements); +uint32 getDeclaration(void *declaration, VertexElement *elements); + +void *destroyNativeData(void *object, int32, int32); +Stream *readNativeData(Stream *stream, int32 len, void *object, int32, int32); +Stream *writeNativeData(Stream *stream, int32 len, void *object, int32, int32); +int32 getSizeNativeData(void *object, int32, int32); +void registerNativeDataPlugin(void); + +class ObjPipeline : public rw::ObjPipeline +{ +public: + void (*instanceCB)(Geometry *geo, InstanceDataHeader *header); + void (*uninstanceCB)(Geometry *geo, InstanceDataHeader *header); + void (*renderCB)(Atomic *atomic, InstanceDataHeader *header); + + ObjPipeline(uint32 platform); +}; + +void defaultInstanceCB(Geometry *geo, InstanceDataHeader *header); +void defaultUninstanceCB(Geometry *geo, InstanceDataHeader *header); +void defaultRenderCB(Atomic *atomic, InstanceDataHeader *header); + +ObjPipeline *makeDefaultPipeline(void); + +ObjPipeline *makeSkinPipeline(void); + +ObjPipeline *makeMatFXPipeline(void); + +// Native Texture and Raster + +Texture *readNativeTexture(Stream *stream); +void writeNativeTexture(Texture *tex, Stream *stream); +uint32 getSizeNativeTexture(Texture *tex); + +} +} diff --git a/src/d3d/rwxbox.h b/src/d3d/rwxbox.h new file mode 100644 index 0000000..1c16af1 --- /dev/null +++ b/src/d3d/rwxbox.h @@ -0,0 +1,181 @@ +namespace rw { +namespace xbox { + +void initializePlatform(void); + +struct InstanceData +{ + uint32 minVert; + int32 numVertices; + int32 numIndices; + void *indexBuffer; + Material *material; + uint32 vertexShader; +}; + +struct InstanceDataHeader : rw::InstanceDataHeader +{ + int32 size; + uint16 serialNumber; + uint16 numMeshes; + uint32 primType; + int32 numVertices; + int32 stride; + void *vertexBuffer; + bool32 vertexAlpha; + InstanceData *begin; + InstanceData *end; + + uint8 *data; +}; + +void *destroyNativeData(void *object, int32, int32); +Stream *readNativeData(Stream *stream, int32 len, void *object, int32, int32); +Stream *writeNativeData(Stream *stream, int32 len, void *object, int32, int32); +int32 getSizeNativeData(void *object, int32, int32); +void registerNativeDataPlugin(void); + +class ObjPipeline : public rw::ObjPipeline +{ +public: + void (*instanceCB)(Geometry *geo, InstanceDataHeader *header); + void (*uninstanceCB)(Geometry *geo, InstanceDataHeader *header); + + ObjPipeline(uint32 platform); +}; + +ObjPipeline *makeDefaultPipeline(void); + +// Skin plugin + +Stream *readNativeSkin(Stream *stream, int32, void *object, int32 offset); +Stream *writeNativeSkin(Stream *stream, int32 len, void *object, int32 offset); +int32 getSizeNativeSkin(void *object, int32 offset); + +ObjPipeline *makeSkinPipeline(void); + +ObjPipeline *makeMatFXPipeline(void); + +// Vertex Format plugin + +extern uint32 vertexFormatSizes[6]; + +uint32 *getVertexFmt(Geometry *g); +uint32 makeVertexFmt(int32 flags, uint32 numTexSets); +uint32 getVertexFmtStride(uint32 fmt); + +void registerVertexFormatPlugin(void); + +// Native Texture and Raster + +struct XboxRaster +{ + void *texture; + void *palette; + uint32 format; + bool32 hasAlpha; + bool32 unknownFlag; +}; + +int32 getLevelSize(Raster *raster, int32 level); + +extern int32 nativeRasterOffset; +void registerNativeRaster(void); + +Texture *readNativeTexture(Stream *stream); +void writeNativeTexture(Texture *tex, Stream *stream); +uint32 getSizeNativeTexture(Texture *tex); + +enum { + D3DFMT_UNKNOWN = 0xFFFFFFFF, + + /* Swizzled formats */ + + D3DFMT_A8R8G8B8 = 0x00000006, + D3DFMT_X8R8G8B8 = 0x00000007, + D3DFMT_R5G6B5 = 0x00000005, + D3DFMT_R6G5B5 = 0x00000027, + D3DFMT_X1R5G5B5 = 0x00000003, + D3DFMT_A1R5G5B5 = 0x00000002, + D3DFMT_A4R4G4B4 = 0x00000004, + D3DFMT_A8 = 0x00000019, + D3DFMT_A8B8G8R8 = 0x0000003A, + D3DFMT_B8G8R8A8 = 0x0000003B, + D3DFMT_R4G4B4A4 = 0x00000039, + D3DFMT_R5G5B5A1 = 0x00000038, + D3DFMT_R8G8B8A8 = 0x0000003C, + D3DFMT_R8B8 = 0x00000029, + D3DFMT_G8B8 = 0x00000028, + + D3DFMT_P8 = 0x0000000B, + + D3DFMT_L8 = 0x00000000, + D3DFMT_A8L8 = 0x0000001A, + D3DFMT_AL8 = 0x00000001, + D3DFMT_L16 = 0x00000032, + + D3DFMT_V8U8 = 0x00000028, + D3DFMT_L6V5U5 = 0x00000027, + D3DFMT_X8L8V8U8 = 0x00000007, + D3DFMT_Q8W8V8U8 = 0x0000003A, + D3DFMT_V16U16 = 0x00000033, + + D3DFMT_D16_LOCKABLE = 0x0000002C, + D3DFMT_D16 = 0x0000002C, + D3DFMT_D24S8 = 0x0000002A, + D3DFMT_F16 = 0x0000002D, + D3DFMT_F24S8 = 0x0000002B, + + /* YUV formats */ + + D3DFMT_YUY2 = 0x00000024, + D3DFMT_UYVY = 0x00000025, + + /* Compressed formats */ + + D3DFMT_DXT1 = 0x0000000C, + D3DFMT_DXT2 = 0x0000000E, + D3DFMT_DXT3 = 0x0000000E, + D3DFMT_DXT4 = 0x0000000F, + D3DFMT_DXT5 = 0x0000000F, + + /* Linear formats */ + + D3DFMT_LIN_A1R5G5B5 = 0x00000010, + D3DFMT_LIN_A4R4G4B4 = 0x0000001D, + D3DFMT_LIN_A8 = 0x0000001F, + D3DFMT_LIN_A8B8G8R8 = 0x0000003F, + D3DFMT_LIN_A8R8G8B8 = 0x00000012, + D3DFMT_LIN_B8G8R8A8 = 0x00000040, + D3DFMT_LIN_G8B8 = 0x00000017, + D3DFMT_LIN_R4G4B4A4 = 0x0000003E, + D3DFMT_LIN_R5G5B5A1 = 0x0000003D, + D3DFMT_LIN_R5G6B5 = 0x00000011, + D3DFMT_LIN_R6G5B5 = 0x00000037, + D3DFMT_LIN_R8B8 = 0x00000016, + D3DFMT_LIN_R8G8B8A8 = 0x00000041, + D3DFMT_LIN_X1R5G5B5 = 0x0000001C, + D3DFMT_LIN_X8R8G8B8 = 0x0000001E, + + D3DFMT_LIN_A8L8 = 0x00000020, + D3DFMT_LIN_AL8 = 0x0000001B, + D3DFMT_LIN_L16 = 0x00000035, + D3DFMT_LIN_L8 = 0x00000013, + + D3DFMT_LIN_V16U16 = 0x00000036, + D3DFMT_LIN_V8U8 = 0x00000017, + D3DFMT_LIN_L6V5U5 = 0x00000037, + D3DFMT_LIN_X8L8V8U8 = 0x0000001E, + D3DFMT_LIN_Q8W8V8U8 = 0x00000012, + + D3DFMT_LIN_D24S8 = 0x0000002E, + D3DFMT_LIN_F24S8 = 0x0000002F, + D3DFMT_LIN_D16 = 0x00000030, + D3DFMT_LIN_F16 = 0x00000031, + + D3DFMT_VERTEXDATA = 100, + D3DFMT_INDEX16 = 101, +}; + +} +} diff --git a/src/d3d/xbox.cpp b/src/d3d/xbox.cpp new file mode 100644 index 0000000..e0efaba --- /dev/null +++ b/src/d3d/xbox.cpp @@ -0,0 +1,1050 @@ +#include +#include +#include +#include + +#include "../rwbase.h" +#include "../rwerror.h" +#include "../rwplg.h" +#include "../rwpipeline.h" +#include "../rwobjects.h" +#include "../rwengine.h" +#include "../rwplugins.h" +#include "rwxbox.h" + +#define PLUGIN_ID 2 + +namespace rw { +namespace xbox { + +void +initializePlatform(void) +{ + driver[PLATFORM_XBOX].defaultPipeline = makeDefaultPipeline(); +} + +void* +destroyNativeData(void *object, int32, int32) +{ + Geometry *geometry = (Geometry*)object; + if(geometry->instData == nil || + geometry->instData->platform != PLATFORM_XBOX) + return object; + InstanceDataHeader *header = + (InstanceDataHeader*)geometry->instData; + geometry->instData = nil; + delete[] (uint8*)header->vertexBuffer; + delete[] header->begin; + delete[] header->data; + delete header; + return object; +} + +Stream* +readNativeData(Stream *stream, int32, void *object, int32, int32) +{ + Geometry *geometry = (Geometry*)object; + uint32 vers; + uint32 platform; + if(!findChunk(stream, ID_STRUCT, nil, &vers)){ + RWERROR((ERR_CHUNK, "STRUCT")) + return nil; + } + platform = stream->readU32(); + if(platform != PLATFORM_XBOX){ + RWERROR((ERR_PLATFORM, platform)); + return nil; + } + if(vers < 0x35000){ + RWERROR((ERR_VERSION, vers)); + return nil; + } + InstanceDataHeader *header = new InstanceDataHeader; + geometry->instData = header; + header->platform = PLATFORM_XBOX; + + int32 size = stream->readI32(); + // The 0x18 byte are the resentryheader. + // We don't have it but it's used for alignment. + header->data = new uint8[size + 0x18]; + uint8 *p = header->data+0x18+4; + stream->read(p, size-4); + + header->size = size; + header->serialNumber = *(uint16*)p; p += 2; + header->numMeshes = *(uint16*)p; p += 2; + header->primType = *(uint32*)p; p += 4; + header->numVertices = *(uint32*)p; p += 4; + header->stride = *(uint32*)p; p += 4; + // RxXboxVertexFormat in 3.3 here + p += 4; // skip vertexBuffer pointer + header->vertexAlpha = *(bool32*)p; p += 4; + p += 8; // skip begin, end pointers + + InstanceData *inst = new InstanceData[header->numMeshes]; + header->begin = inst; + for(int i = 0; i < header->numMeshes; i++){ + inst->minVert = *(uint32*)p; p += 4; + inst->numVertices = *(int32*)p; p += 4; + inst->numIndices = *(int32*)p; p += 4; + inst->indexBuffer = header->data + *(uint32*)p; p += 4; + p += 8; // skip material and vertexShader + inst->vertexShader = 0; + // pixelShader in 3.3 here + inst++; + } + header->end = inst; + + header->vertexBuffer = new uint8[header->stride*header->numVertices]; + stream->read(header->vertexBuffer, header->stride*header->numVertices); + return stream; +} + +Stream* +writeNativeData(Stream *stream, int32 len, void *object, int32, int32) +{ + Geometry *geometry = (Geometry*)object; + writeChunkHeader(stream, ID_STRUCT, len-12); + if(geometry->instData == nil || + geometry->instData->platform != PLATFORM_XBOX) + return stream; + stream->writeU32(PLATFORM_XBOX); + assert(rw::version >= 0x35000 && "can't write native Xbox data < 0x35000"); + InstanceDataHeader *header = (InstanceDataHeader*)geometry->instData; + + // we just fill header->data and write that + uint8 *p = header->data+0x18; + *(int32*)p = header->size; p += 4; + *(uint16*)p = header->serialNumber; p += 2; + *(uint16*)p = header->numMeshes; p += 2; + *(uint32*)p = header->primType; p += 4; + *(uint32*)p = header->numVertices; p += 4; + *(uint32*)p = header->stride; p += 4; + // RxXboxVertexFormat in 3.3 here + p += 4; // skip vertexBuffer pointer + *(bool32*)p = header->vertexAlpha; p += 4; + p += 8; // skip begin, end pointers + + InstanceData *inst = header->begin; + for(int i = 0; i < header->numMeshes; i++){ + *(uint32*)p = inst->minVert; p += 4; + *(int32*)p = inst->numVertices; p += 4; + *(int32*)p = inst->numIndices; p += 4; + *(uint32*)p = (uint8*)inst->indexBuffer - header->data; p += 4; + p += 8; // skip material and vertexShader + // pixelShader in 3.3 here + inst++; + } + + stream->write(header->data+0x18, header->size); + stream->write(header->vertexBuffer, header->stride*header->numVertices); + return stream; +} + +int32 +getSizeNativeData(void *object, int32, int32) +{ + Geometry *geometry = (Geometry*)object; + if(geometry->instData == nil || + geometry->instData->platform != PLATFORM_XBOX) + return 0; + InstanceDataHeader *header = (InstanceDataHeader*)geometry->instData; + return 12 + 4 + header->size + header->stride*header->numVertices; +} + +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) +{ + enum { + D3DPT_TRIANGLELIST = 5, + D3DPT_TRIANGLESTRIP = 6, + }; + + ObjPipeline *pipe = (ObjPipeline*)rwpipe; + Geometry *geo = atomic->geometry; + if(geo->geoflags & Geometry::NATIVE) + return; + geo->geoflags |= Geometry::NATIVE; + InstanceDataHeader *header = new InstanceDataHeader; + MeshHeader *meshh = geo->meshHeader; + geo->instData = header; + header->platform = PLATFORM_XBOX; + + header->size = 0x24 + meshh->numMeshes*0x18 + 0x10; + Mesh *mesh = meshh->mesh; + for(uint32 i = 0; i < meshh->numMeshes; i++) + header->size += (mesh++->numIndices*2 + 0xF) & ~0xF; + // The 0x18 byte are the resentryheader. + // We don't have it but it's used for alignment. + header->data = new uint8[header->size + 0x18]; + header->serialNumber = 0; + header->numMeshes = meshh->numMeshes; + header->primType = meshh->flags == 1 ? D3DPT_TRIANGLESTRIP : D3DPT_TRIANGLELIST; + header->numVertices = geo->numVertices; + header->vertexAlpha = 0; + // set by the instanceCB + header->stride = 0; + header->vertexBuffer = nil; + + InstanceData *inst = new InstanceData[header->numMeshes]; + header->begin = inst; + mesh = meshh->mesh; + uint8 *indexbuf = (uint8*)header->data + ((0x18 + 0x24 + header->numMeshes*0x18 + 0xF)&~0xF); + for(uint32 i = 0; i < header->numMeshes; i++){ + findMinVertAndNumVertices(mesh->indices, mesh->numIndices, + &inst->minVert, &inst->numVertices); + inst->numIndices = mesh->numIndices; + inst->indexBuffer = indexbuf; + memcpy(inst->indexBuffer, mesh->indices, inst->numIndices*sizeof(uint16)); + indexbuf += (inst->numIndices*2 + 0xF) & ~0xF; + inst->material = mesh->material; + inst->vertexShader = 0; // TODO? + mesh++; + inst++; + } + header->end = inst; + + pipe->instanceCB(geo, header); +} + +static void +uninstance(rw::ObjPipeline *rwpipe, Atomic *atomic) +{ + ObjPipeline *pipe = (ObjPipeline*)rwpipe; + Geometry *geo = atomic->geometry; + if((geo->geoflags & Geometry::NATIVE) == 0) + return; + assert(geo->instData != nil); + assert(geo->instData->platform == PLATFORM_XBOX); + geo->geoflags &= ~Geometry::NATIVE; + geo->allocateData(); + geo->meshHeader->allocateIndices(); + + InstanceDataHeader *header = (InstanceDataHeader*)geo->instData; + InstanceData *inst = header->begin; + Mesh *mesh = geo->meshHeader->mesh; + for(uint32 i = 0; i < header->numMeshes; i++){ + uint16 *indices = (uint16*)inst->indexBuffer; + memcpy(mesh->indices, indices, inst->numIndices*2); + mesh++; + inst++; + } + + pipe->uninstanceCB(geo, header); + geo->generateTriangles(); + destroyNativeData(geo, 0, 0); +} + +ObjPipeline::ObjPipeline(uint32 platform) + : rw::ObjPipeline(platform) +{ + this->impl.instance = xbox::instance; + this->impl.uninstance = xbox::uninstance; + this->instanceCB = nil; + this->uninstanceCB = nil; +} + + +int v3dFormatMap[] = { + -1, VERT_BYTE3, VERT_SHORT3, VERT_NORMSHORT3, VERT_COMPNORM, VERT_FLOAT3 +}; + +int v2dFormatMap[] = { + -1, VERT_BYTE2, VERT_SHORT2, VERT_NORMSHORT2, VERT_COMPNORM, VERT_FLOAT2 +}; + +void +defaultInstanceCB(Geometry *geo, InstanceDataHeader *header) +{ + uint32 *vertexFmt = getVertexFmt(geo); + if(*vertexFmt == 0) + *vertexFmt = makeVertexFmt(geo->geoflags, geo->numTexCoordSets); + header->stride = getVertexFmtStride(*vertexFmt); + header->vertexBuffer = new uint8[header->stride*header->numVertices]; + uint8 *dst = (uint8*)header->vertexBuffer; + + uint32 fmt = *vertexFmt; + uint32 sel = fmt & 0xF; + instV3d(v3dFormatMap[sel], dst, geo->morphTargets[0].vertices, + header->numVertices, header->stride); + dst += sel == 4 ? 4 : 3*vertexFormatSizes[sel]; + + sel = (fmt >> 4) & 0xF; + if(sel){ + instV3d(v3dFormatMap[sel], dst, geo->morphTargets[0].normals, + header->numVertices, header->stride); + dst += sel == 4 ? 4 : 3*vertexFormatSizes[sel]; + } + + if(fmt & 0x1000000){ + header->vertexAlpha = instColor(VERT_ARGB, dst, geo->colors, + header->numVertices, header->stride); + dst += 4; + } + + for(int i = 0; i < 4; i++){ + sel = (fmt >> (i*4 + 8)) & 0xF; + if(sel == 0) + break; + instV2d(v2dFormatMap[sel], dst, geo->texCoords[i], + header->numVertices, header->stride); + dst += sel == 4 ? 4 : 2*vertexFormatSizes[sel]; + } + + if(fmt & 0xE000000) + assert(0 && "can't instance tangents or whatever it is"); +} + +void +defaultUninstanceCB(Geometry *geo, InstanceDataHeader *header) +{ + uint32 *vertexFmt = getVertexFmt(geo); + uint32 fmt = *vertexFmt; + assert(fmt != 0); + uint8 *src = (uint8*)header->vertexBuffer; + + uint32 sel = fmt & 0xF; + uninstV3d(v3dFormatMap[sel], geo->morphTargets[0].vertices, src, + header->numVertices, header->stride); + src += sel == 4 ? 4 : 3*vertexFormatSizes[sel]; + + sel = (fmt >> 4) & 0xF; + if(sel){ + uninstV3d(v3dFormatMap[sel], geo->morphTargets[0].normals, src, + header->numVertices, header->stride); + src += sel == 4 ? 4 : 3*vertexFormatSizes[sel]; + } + + if(fmt & 0x1000000){ + uninstColor(VERT_ARGB, geo->colors, src, + header->numVertices, header->stride); + src += 4; + } + + for(int i = 0; i < 4; i++){ + sel = (fmt >> (i*4 + 8)) & 0xF; + if(sel == 0) + break; + uninstV2d(v2dFormatMap[sel], geo->texCoords[i], src, + header->numVertices, header->stride); + src += sel == 4 ? 4 : 2*vertexFormatSizes[sel]; + } +} + +ObjPipeline* +makeDefaultPipeline(void) +{ + ObjPipeline *pipe = new ObjPipeline(PLATFORM_XBOX); + pipe->instanceCB = defaultInstanceCB; + pipe->uninstanceCB = defaultUninstanceCB; + return pipe; +} + +// Skin plugin + +struct NativeSkin +{ + int32 table1[256]; // maps indices to bones + int32 table2[256]; // maps bones to indices + int32 numUsedBones; + void *vertexBuffer; + int32 stride; +}; + +Stream* +readNativeSkin(Stream *stream, int32, void *object, int32 offset) +{ + Geometry *geometry = (Geometry*)object; + uint32 vers, platform; + if(!findChunk(stream, ID_STRUCT, nil, &vers)){ + RWERROR((ERR_CHUNK, "STRUCT")) + return nil; + } + platform = stream->readU32(); + if(platform != PLATFORM_XBOX){ + RWERROR((ERR_PLATFORM, platform)); + return nil; + } + if(vers < 0x35000){ + RWERROR((ERR_VERSION, vers)); + return nil; + } + + Skin *skin = new Skin; + *PLUGINOFFSET(Skin*, geometry, offset) = skin; + + int32 numBones = stream->readI32(); + skin->init(numBones, 0, 0); + NativeSkin *natskin = new NativeSkin; + skin->platformData = natskin; + stream->read(natskin->table1, 256*sizeof(int32)); + stream->read(natskin->table2, 256*sizeof(int32)); + natskin->numUsedBones = stream->readI32(); + skin->numWeights = stream->readI32(); + stream->seek(4); // skip pointer to vertexBuffer + natskin->stride = stream->readI32(); + int32 size = geometry->numVertices*natskin->stride; + natskin->vertexBuffer = new uint8[size]; + stream->read(natskin->vertexBuffer, size); + stream->read(skin->inverseMatrices, skin->numBones*64); + + // no split skins in GTA + stream->seek(12); + return stream; +} + +Stream* +writeNativeSkin(Stream *stream, int32 len, void *object, int32 offset) +{ + Geometry *geometry = (Geometry*)object; + Skin *skin = *PLUGINOFFSET(Skin*, object, offset); + assert(skin->platformData); + assert(rw::version >= 0x35000 && "can't handle native xbox skin < 0x35000"); + NativeSkin *natskin = (NativeSkin*)skin->platformData; + + writeChunkHeader(stream, ID_STRUCT, len-12); + stream->writeU32(PLATFORM_XBOX); + stream->writeI32(skin->numBones); + stream->write(natskin->table1, 256*sizeof(int32)); + stream->write(natskin->table2, 256*sizeof(int32)); + stream->writeI32(natskin->numUsedBones); + stream->writeI32(skin->numWeights); + stream->writeU32(0xBADEAFFE); // pointer to vertexBuffer + stream->writeI32(natskin->stride); + stream->write(natskin->vertexBuffer, + geometry->numVertices*natskin->stride); + stream->write(skin->inverseMatrices, skin->numBones*64); + int32 buffer[3] = { 0, 0, 0}; + stream->write(buffer, 12); + return stream; +} + +int32 +getSizeNativeSkin(void *object, int32 offset) +{ + Geometry *geometry = (Geometry*)object; + Skin *skin = *PLUGINOFFSET(Skin*, object, offset); + if(skin == nil) + return -1; + if(skin->platformData == nil) + return -1; + NativeSkin *natskin = (NativeSkin*)skin->platformData; + return 12 + 8 + 2*256*4 + 4*4 + + natskin->stride*geometry->numVertices + skin->numBones*64 + 12; +} + +void +skinInstanceCB(Geometry *geo, InstanceDataHeader *header) +{ + defaultInstanceCB(geo, header); + + Skin *skin = *PLUGINOFFSET(Skin*, geo, skinGlobals.offset); + if(skin == nil) + return; + NativeSkin *natskin = new NativeSkin; + skin->platformData = natskin; + + natskin->numUsedBones = skin->numUsedBones; + memset(natskin->table1, 0xFF, sizeof(natskin->table1)); + memset(natskin->table2, 0x00, sizeof(natskin->table2)); + for(int32 i = 0; i < skin->numUsedBones; i++){ + natskin->table1[i] = skin->usedBones[i]; + natskin->table2[skin->usedBones[i]] = i; + } + + natskin->stride = 3*skin->numWeights; + uint8 *vbuf = new uint8[header->numVertices*natskin->stride]; + natskin->vertexBuffer = vbuf; + + int32 w[4]; + int sum; + float *weights = skin->weights; + uint8 *p = vbuf; + int32 numVertices = header->numVertices; + while(numVertices--){ + sum = 0; + for(int i = 1; i < skin->numWeights; i++){ + w[i] = weights[i]*255.0f + 0.5f; + sum += w[i]; + } + w[0] = 255 - sum; + for(int i = 0; i < skin->numWeights; i++) + p[i] = w[i]; + p += natskin->stride; + weights += 4; + } + + numVertices = header->numVertices; + p = vbuf + skin->numWeights; + uint8 *indices = skin->indices; + uint16 *idx; + while(numVertices--){ + idx = (uint16*)p; + for(int i = 0; i < skin->numWeights; i++) + idx[i] = 3*natskin->table2[indices[i]]; + p += natskin->stride; + indices += 4; + } +} + +void +skinUninstanceCB(Geometry *geo, InstanceDataHeader *header) +{ + defaultUninstanceCB(geo, header); + + Skin *skin = *PLUGINOFFSET(Skin*, geo, skinGlobals.offset); + if(skin == nil) + return; + NativeSkin *natskin = (NativeSkin*)skin->platformData; + + uint8 *data = skin->data; + float *invMats = skin->inverseMatrices; + skin->init(skin->numBones, natskin->numUsedBones, geo->numVertices); + memcpy(skin->inverseMatrices, invMats, skin->numBones*64); + delete[] data; + + for(int32 j = 0; j < skin->numUsedBones; j++) + skin->usedBones[j] = natskin->table1[j]; + + float *weights = skin->weights; + uint8 *indices = skin->indices; + uint8 *p = (uint8*)natskin->vertexBuffer; + int32 numVertices = header->numVertices; + float w[4]; + uint8 i[4]; + uint16 *ip; + while(numVertices--){ + w[0] = w[1] = w[2] = w[3] = 0.0f; + i[0] = i[1] = i[2] = i[3] = 0; + + for(int32 j = 0; j < skin->numWeights; j++) + w[j] = *p++/255.0f; + + ip = (uint16*)p; + for(int32 j = 0; j < skin->numWeights; j++){ + i[j] = natskin->table1[*ip++/3]; + if(w[j] == 0.0f) i[j] = 0; // clean up a bit + } + p = (uint8*)ip; + + for(int32 j = 0; j < 4; j++){ + *weights++ = w[j]; + *indices++ = i[j]; + } + } + + delete[] (uint8*)natskin->vertexBuffer; + delete natskin; +} + +ObjPipeline* +makeSkinPipeline(void) +{ + ObjPipeline *pipe = new ObjPipeline(PLATFORM_XBOX); + pipe->instanceCB = skinInstanceCB; + pipe->uninstanceCB = skinUninstanceCB; + pipe->pluginID = ID_SKIN; + pipe->pluginData = 1; + return pipe; +} + +ObjPipeline* +makeMatFXPipeline(void) +{ + ObjPipeline *pipe = new ObjPipeline(PLATFORM_XBOX); + pipe->instanceCB = defaultInstanceCB; + pipe->uninstanceCB = defaultUninstanceCB; + pipe->pluginID = ID_MATFX; + pipe->pluginData = 0; + return pipe; +} + +// Vertex Format Plugin + +static int32 vertexFmtOffset; + +uint32 vertexFormatSizes[6] = { + 0, 1, 2, 2, 4, 4 +}; + +uint32* +getVertexFmt(Geometry *g) +{ + return PLUGINOFFSET(uint32, g, vertexFmtOffset); +} + +uint32 +makeVertexFmt(int32 flags, uint32 numTexSets) +{ + if(numTexSets > 4) + numTexSets = 4; + uint32 fmt = 0x5; // FLOAT3 + if(flags & Geometry::NORMALS) + fmt |= 0x40; // NORMPACKED3 + for(uint32 i = 0; i < numTexSets; i++) + fmt |= 0x500 << i*4; // FLOAT2 + if(flags & Geometry::PRELIT) + fmt |= 0x1000000; // D3DCOLOR + return fmt; +} + +uint32 +getVertexFmtStride(uint32 fmt) +{ + uint32 stride = 0; + uint32 v = fmt & 0xF; + uint32 n = (fmt >> 4) & 0xF; + stride += v == 4 ? 4 : 3*vertexFormatSizes[v]; + stride += n == 4 ? 4 : 3*vertexFormatSizes[n]; + if(fmt & 0x1000000) + stride += 4; + for(int i = 0; i < 4; i++){ + uint32 t = (fmt >> (i*4 + 8)) & 0xF; + stride += t == 4 ? 4 : 2*vertexFormatSizes[t]; + } + if(fmt & 0xE000000) + stride += 8; + return stride; +} + +static void* +createVertexFmt(void *object, int32 offset, int32) +{ + *PLUGINOFFSET(uint32, object, offset) = 0; + return object; +} + +static void* +copyVertexFmt(void *dst, void *src, int32 offset, int32) +{ + *PLUGINOFFSET(uint32, dst, offset) = *PLUGINOFFSET(uint32, src, offset); + return dst; +} + +static Stream* +readVertexFmt(Stream *stream, int32, void *object, int32 offset, int32) +{ + uint32 fmt = stream->readU32(); + *PLUGINOFFSET(uint32, object, offset) = fmt; + // TODO: ? create and attach "vertex shader" + return stream; +} + +static Stream* +writeVertexFmt(Stream *stream, int32, void *object, int32 offset, int32) +{ + stream->writeI32(*PLUGINOFFSET(uint32, object, offset)); + return stream; +} + +static int32 +getSizeVertexFmt(void*, int32, int32) +{ + if(rw::platform != PLATFORM_XBOX) + return -1; + return 4; +} + +void +registerVertexFormatPlugin(void) +{ + vertexFmtOffset = Geometry::registerPlugin(sizeof(uint32), ID_VERTEXFMT, + createVertexFmt, nil, copyVertexFmt); + Geometry::registerPluginStream(ID_VERTEXFMT, + readVertexFmt, + writeVertexFmt, + getSizeVertexFmt); +} + +// Native Texture and Raster + +int32 nativeRasterOffset; + +static uint32 +calculateTextureSize(uint32 width, uint32 height, uint32 depth, uint32 format) +{ +#define D3DFMT_W11V11U10 65 + switch(format){ + default: + case D3DFMT_UNKNOWN: + return 0; + case D3DFMT_A8: + case D3DFMT_P8: + case D3DFMT_L8: + case D3DFMT_AL8: + case D3DFMT_LIN_A8: + case D3DFMT_LIN_AL8: + case D3DFMT_LIN_L8: + return width * height * depth; + case D3DFMT_R5G6B5: + case D3DFMT_R6G5B5: + case D3DFMT_X1R5G5B5: + case D3DFMT_A1R5G5B5: + case D3DFMT_A4R4G4B4: + case D3DFMT_R4G4B4A4: + case D3DFMT_R5G5B5A1: + case D3DFMT_R8B8: + case D3DFMT_G8B8: + case D3DFMT_A8L8: + case D3DFMT_L16: + //case D3DFMT_V8U8: + //case D3DFMT_L6V5U5: + case D3DFMT_D16_LOCKABLE: + //case D3DFMT_D16: + case D3DFMT_F16: + case D3DFMT_YUY2: + case D3DFMT_UYVY: + case D3DFMT_LIN_A1R5G5B5: + case D3DFMT_LIN_A4R4G4B4: + case D3DFMT_LIN_G8B8: + case D3DFMT_LIN_R4G4B4A4: + case D3DFMT_LIN_R5G5B5A1: + case D3DFMT_LIN_R5G6B5: + case D3DFMT_LIN_R6G5B5: + case D3DFMT_LIN_R8B8: + case D3DFMT_LIN_X1R5G5B5: + case D3DFMT_LIN_A8L8: + case D3DFMT_LIN_L16: + //case D3DFMT_LIN_V8U8: + //case D3DFMT_LIN_L6V5U5: + case D3DFMT_LIN_D16: + case D3DFMT_LIN_F16: + return width * 2 * height * depth; + case D3DFMT_A8R8G8B8: + case D3DFMT_X8R8G8B8: + case D3DFMT_A8B8G8R8: + case D3DFMT_B8G8R8A8: + case D3DFMT_R8G8B8A8: + //case D3DFMT_X8L8V8U8: + //case D3DFMT_Q8W8V8U8: + case D3DFMT_V16U16: + case D3DFMT_D24S8: + case D3DFMT_F24S8: + case D3DFMT_LIN_A8B8G8R8: + case D3DFMT_LIN_A8R8G8B8: + case D3DFMT_LIN_B8G8R8A8: + case D3DFMT_LIN_R8G8B8A8: + case D3DFMT_LIN_X8R8G8B8: + case D3DFMT_LIN_V16U16: + //case D3DFMT_LIN_X8L8V8U8: + //case D3DFMT_LIN_Q8W8V8U8: + case D3DFMT_LIN_D24S8: + case D3DFMT_LIN_F24S8: + return width * 4 * height * depth; + case D3DFMT_DXT1: + assert(depth <= 1); + return ((width + 3) >> 2) * ((height + 3) >> 2) * 8; + //case D3DFMT_DXT2: + case D3DFMT_DXT3: + //case D3DFMT_DXT4: + case D3DFMT_DXT5: + assert(depth <= 1); + return ((width + 3) >> 2) * ((height + 3) >> 2) * 16; + } +} + +static void* +createTexture(int32 width, int32 height, int32 numlevels, uint32 format) +{ + int32 w = width; + int32 h = height; + int32 size = 0; + for(int32 i = 0; i < numlevels; i++){ + size += calculateTextureSize(w, h, 1, format); + w /= 2; + if(w == 0) w = 1; + h /= 2; + if(h == 0) h = 1; + } + size = (size+3)&~3; + uint8 *data = new uint8[sizeof(RasterLevels)+sizeof(RasterLevels::Level)*(numlevels-1)+size]; + RasterLevels *levels = (RasterLevels*)data; + data += sizeof(RasterLevels)+sizeof(RasterLevels::Level)*(numlevels-1); + levels->numlevels = numlevels; + levels->format = format; + w = width; + h = height; + for(int32 i = 0; i < numlevels; i++){ + levels->levels[i].width = w; + levels->levels[i].height = h; + levels->levels[i].data = data; + levels->levels[i].size = calculateTextureSize(w, h, 1, format); + data += levels->levels[i].size; + w /= 2; + if(w == 0) w = 1; + h /= 2; + if(h == 0) h = 1; + } + return levels; +} + +static void +rasterCreate(Raster *raster) +{ + XboxRaster *natras = PLUGINOFFSET(XboxRaster, raster, nativeRasterOffset); + static uint32 formatMap[] = { + D3DFMT_UNKNOWN, + D3DFMT_A1R5G5B5, + D3DFMT_R5G6B5, + D3DFMT_A4R4G4B4, + D3DFMT_L8, + D3DFMT_A8R8G8B8, + D3DFMT_X8R8G8B8, + D3DFMT_UNKNOWN, + D3DFMT_UNKNOWN, + D3DFMT_UNKNOWN, + D3DFMT_X1R5G5B5, + D3DFMT_UNKNOWN, + D3DFMT_UNKNOWN, + D3DFMT_UNKNOWN, + D3DFMT_UNKNOWN, + D3DFMT_UNKNOWN + }; + static bool32 alphaMap[] = { + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 0, 0, 0, + 0, + 0, 0, 0, 0, 0 + }; + if(raster->flags & 0x80) + return; + uint32 format; + if(raster->format & (Raster::PAL4 | Raster::PAL8)){ + format = D3DFMT_P8; + natras->palette = new uint8[4*256]; + }else + format = formatMap[(raster->format >> 8) & 0xF]; + natras->format = 0; + natras->hasAlpha = alphaMap[(raster->format >> 8) & 0xF]; + int32 levels = Raster::calculateNumLevels(raster->width, raster->height); + natras->texture = createTexture(raster->width, raster->height, + raster->format & Raster::MIPMAP ? levels : 1, + format); +} + +static uint8* +rasterLock(Raster *raster, int32 level) +{ + XboxRaster *natras = PLUGINOFFSET(XboxRaster, raster, nativeRasterOffset); + RasterLevels *levels = (RasterLevels*)natras->texture; + return levels->levels[level].data; +} + +static void +rasterUnlock(Raster*, int32) +{ +} + +static int32 +rasterNumLevels(Raster *raster) +{ + XboxRaster *natras = PLUGINOFFSET(XboxRaster, raster, nativeRasterOffset); + RasterLevels *levels = (RasterLevels*)natras->texture; + return levels->numlevels; +} + +int32 +getLevelSize(Raster *raster, int32 level) +{ + XboxRaster *ras = PLUGINOFFSET(XboxRaster, raster, nativeRasterOffset); + RasterLevels *levels = (RasterLevels*)ras->texture; + return levels->levels[level].size; +} + +static void* +createNativeRaster(void *object, int32 offset, int32) +{ + XboxRaster *raster = PLUGINOFFSET(XboxRaster, object, offset); + raster->texture = nil; + raster->palette = nil; + raster->format = 0; + raster->hasAlpha = 0; + raster->unknownFlag = 0; + return object; +} + +static void* +destroyNativeRaster(void *object, int32, int32) +{ + // TODO: + return object; +} + +static void* +copyNativeRaster(void *dst, void *, int32 offset, int32) +{ + XboxRaster *raster = PLUGINOFFSET(XboxRaster, dst, offset); + raster->texture = nil; + raster->palette = nil; + raster->format = 0; + raster->hasAlpha = 0; + raster->unknownFlag = 0; + return dst; +} + +void +registerNativeRaster(void) +{ + nativeRasterOffset = Raster::registerPlugin(sizeof(XboxRaster), + 0x12340000 | PLATFORM_XBOX, + createNativeRaster, + destroyNativeRaster, + copyNativeRaster); + driver[PLATFORM_XBOX].rasterNativeOffset = nativeRasterOffset; + driver[PLATFORM_XBOX].rasterCreate = rasterCreate; + driver[PLATFORM_XBOX].rasterLock = rasterLock; + driver[PLATFORM_XBOX].rasterUnlock = rasterUnlock; + driver[PLATFORM_XBOX].rasterNumLevels = rasterNumLevels; +} + +Texture* +readNativeTexture(Stream *stream) +{ + uint32 vers, platform; + if(!findChunk(stream, ID_STRUCT, nil, &vers)){ + RWERROR((ERR_CHUNK, "STRUCT")) + return nil; + } + platform = stream->readU32(); + if(platform != PLATFORM_XBOX){ + RWERROR((ERR_PLATFORM, platform)); + return nil; + } + if(version < 0x34001){ + RWERROR((ERR_VERSION, version)); + return nil; + } + Texture *tex = Texture::create(nil); + if(tex == nil) + return nil; + + + // Texture + tex->filterAddressing = stream->readU32(); + stream->read(tex->name, 32); + stream->read(tex->mask, 32); + + // Raster + int32 format = stream->readI32(); + bool32 hasAlpha = stream->readI16(); + bool32 unknownFlag = stream->readI16(); + 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 totalSize = stream->readI32(); + + assert(unknownFlag == 0); + Raster *raster; + if(compression){ + raster = Raster::create(width, height, depth, format | type | 0x80, PLATFORM_XBOX); + XboxRaster *ras = PLUGINOFFSET(XboxRaster, raster, nativeRasterOffset); + ras->format = compression; + ras->hasAlpha = hasAlpha; + ras->texture = createTexture(raster->width, raster->height, + raster->format & Raster::MIPMAP ? numLevels : 1, + ras->format); + raster->flags &= ~0x80; + }else + raster = Raster::create(width, height, depth, format | type, PLATFORM_XBOX); + XboxRaster *ras = PLUGINOFFSET(XboxRaster, raster, nativeRasterOffset); + tex->raster = raster; + + if(raster->format & Raster::PAL4) + stream->read(ras->palette, 4*32); + else if(raster->format & Raster::PAL8) + stream->read(ras->palette, 4*256); + + // exploit the fact that mipmaps are allocated consecutively + uint8 *data = raster->lock(0); + stream->read(data, totalSize); + raster->unlock(0); + + tex->streamReadPlugins(stream); + return tex; +} + +void +writeNativeTexture(Texture *tex, Stream *stream) +{ + int32 chunksize = getSizeNativeTexture(tex); + int32 plgsize = tex->streamGetPluginSize(); + writeChunkHeader(stream, ID_TEXTURENATIVE, chunksize); + writeChunkHeader(stream, ID_STRUCT, chunksize-24-plgsize); + stream->writeU32(PLATFORM_XBOX); + + // Texture + stream->writeU32(tex->filterAddressing); + stream->write(tex->name, 32); + stream->write(tex->mask, 32); + + // Raster + Raster *raster = tex->raster; + XboxRaster *ras = PLUGINOFFSET(XboxRaster, raster, nativeRasterOffset); + int32 numLevels = raster->getNumLevels(); + stream->writeI32(raster->format); + stream->writeI16(ras->hasAlpha); + stream->writeI16(ras->unknownFlag); + stream->writeU16(raster->width); + stream->writeU16(raster->height); + stream->writeU8(raster->depth); + stream->writeU8(numLevels); + stream->writeU8(raster->type); + stream->writeU8(ras->format); + + int32 totalSize = 0; + for(int32 i = 0; i < numLevels; i++) + totalSize += getLevelSize(tex->raster, i); + totalSize = (totalSize+3)&~3; + stream->writeI32(totalSize); + + if(raster->format & Raster::PAL4) + stream->write(ras->palette, 4*32); + else if(raster->format & Raster::PAL8) + stream->write(ras->palette, 4*256); + + // exploit the fact that mipmaps are allocated consecutively + uint8 *data = raster->lock(0); + stream->write(data, totalSize); + raster->unlock(0); + + tex->streamWritePlugins(stream); +} + +uint32 +getSizeNativeTexture(Texture *tex) +{ + uint32 size = 12 + 72 + 16 + 4; + int32 levels = tex->raster->getNumLevels(); + for(int32 i = 0; i < levels; i++) + size += getLevelSize(tex->raster, i); + size = (size+3)&~3; + if(tex->raster->format & Raster::PAL4) + size += 4*32; + else if(tex->raster->format & Raster::PAL8) + size += 4*256; + size += 12 + tex->streamGetPluginSize(); + return size; +} + +} +} diff --git a/src/gl/gl3pipe.cpp b/src/gl/gl3pipe.cpp new file mode 100644 index 0000000..af2fb98 --- /dev/null +++ b/src/gl/gl3pipe.cpp @@ -0,0 +1,276 @@ +#include +#include +#include +#include + +#include "../rwbase.h" +#include "../rwerror.h" +#include "../rwplg.h" +#include "../rwpipeline.h" +#include "../rwobjects.h" +#include "../rwengine.h" +#include "../rwplugins.h" +#ifdef RW_OPENGL +#include +#endif +#include "rwgl3.h" +#include "rwgl3shader.h" + +namespace rw { +namespace gl3 { + +// TODO: make some of these things platform-independent + +void +initializePlatform(void) +{ +#ifdef RW_OPENGL + driver[PLATFORM_GL3].defaultPipeline = makeDefaultPipeline(); + matFXGlobals.pipelines[PLATFORM_GL3] = makeMatFXPipeline(); + skinGlobals.pipelines[PLATFORM_GL3] = makeSkinPipeline(); +#endif + + initializeRender(); +} + +#ifdef RW_OPENGL + +static void +instance(rw::ObjPipeline *rwpipe, Atomic *atomic) +{ + ObjPipeline *pipe = (ObjPipeline*)rwpipe; + Geometry *geo = atomic->geometry; + if(geo->geoflags & Geometry::NATIVE) + return; + geo->geoflags |= Geometry::NATIVE; + InstanceDataHeader *header = new InstanceDataHeader; + MeshHeader *meshh = geo->meshHeader; + geo->instData = header; + header->platform = PLATFORM_GL3; + + header->serialNumber = 0; + header->numMeshes = meshh->numMeshes; + header->primType = meshh->flags == 1 ? GL_TRIANGLE_STRIP : GL_TRIANGLES; + header->totalNumVertex = geo->numVertices; + header->totalNumIndex = meshh->totalIndices; + header->inst = new InstanceData[header->numMeshes]; + + header->indexBuffer = new uint16[header->totalNumIndex]; + InstanceData *inst = header->inst; + Mesh *mesh = meshh->mesh; + uint32 offset = 0; + for(uint32 i = 0; i < header->numMeshes; i++){ + findMinVertAndNumVertices(mesh->indices, mesh->numIndices, + &inst->minVert, nil); + inst->numIndex = mesh->numIndices; + inst->material = mesh->material; + inst->vertexAlpha = 0; + inst->program = 0; + inst->offset = offset; + memcpy((uint8*)header->indexBuffer + inst->offset, + mesh->indices, inst->numIndex*2); + offset += inst->numIndex*2; + mesh++; + inst++; + } + + header->vertexBuffer = nil; + header->numAttribs = 0; + header->attribDesc = nil; + header->ibo = 0; + header->vbo = 0; + + glGenBuffers(1, &header->ibo); + glBindBuffer(GL_ARRAY_BUFFER, header->ibo); + glBufferData(GL_ARRAY_BUFFER, header->totalNumIndex*2, + header->indexBuffer, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + pipe->instanceCB(geo, header); +} + +static void +uninstance(rw::ObjPipeline *rwpipe, Atomic *atomic) +{ + assert(0 && "can't uninstance"); +} + +static void +render(rw::ObjPipeline *rwpipe, Atomic *atomic) +{ + ObjPipeline *pipe = (ObjPipeline*)rwpipe; + Geometry *geo = atomic->geometry; + if((geo->geoflags & Geometry::NATIVE) == 0) + pipe->instance(atomic); + assert(geo->instData != nil); + assert(geo->instData->platform == PLATFORM_GL3); + if(pipe->renderCB) + pipe->renderCB(atomic, (InstanceDataHeader*)geo->instData); +} + +ObjPipeline::ObjPipeline(uint32 platform) + : rw::ObjPipeline(platform) +{ + this->impl.instance = gl3::instance; + this->impl.uninstance = gl3::uninstance; + this->impl.render = gl3::render; + this->instanceCB = nil; + this->uninstanceCB = nil; + this->renderCB = nil; +} + +void +defaultInstanceCB(Geometry *geo, InstanceDataHeader *header) +{ + AttribDesc attribs[12], *a; + uint32 stride; + + // + // Create attribute descriptions + // + a = attribs; + stride = 0; + + // Positions + a->index = ATTRIB_POS; + a->size = 3; + a->type = GL_FLOAT; + a->normalized = GL_FALSE; + a->offset = stride; + stride += 12; + a++; + + // Normals + // TODO: compress + bool hasNormals = !!(geo->geoflags & Geometry::NORMALS); + if(hasNormals){ + a->index = ATTRIB_NORMAL; + a->size = 3; + a->type = GL_FLOAT; + a->normalized = GL_FALSE; + a->offset = stride; + stride += 12; + a++; + } + + // Prelighting + bool isPrelit = !!(geo->geoflags & Geometry::PRELIT); + if(isPrelit){ + a->index = ATTRIB_COLOR; + a->size = 4; + a->type = GL_UNSIGNED_BYTE; + a->normalized = GL_TRUE; + a->offset = stride; + stride += 4; + a++; + } + + // Texture coordinates + for(int32 n = 0; n < geo->numTexCoordSets; n++){ + a->index = ATTRIB_TEXCOORDS0+n; + a->size = 2; + a->type = GL_FLOAT; + a->normalized = GL_FALSE; + a->offset = stride; + stride += 8; + a++; + } + + header->numAttribs = a - attribs; + for(a = attribs; a != &attribs[header->numAttribs]; a++) + a->stride = stride; + header->attribDesc = new AttribDesc[header->numAttribs]; + memcpy(header->attribDesc, attribs, + header->numAttribs*sizeof(AttribDesc)); + + // + // Allocate and fill vertex buffer + // + uint8 *verts = new uint8[header->totalNumVertex*stride]; + header->vertexBuffer = verts; + + // Positions + for(a = attribs; a->index != ATTRIB_POS; a++) + ; + instV3d(VERT_FLOAT3, verts + a->offset, + geo->morphTargets[0].vertices, + header->totalNumVertex, a->stride); + + // Normals + if(hasNormals){ + for(a = attribs; a->index != ATTRIB_NORMAL; a++) + ; + instV3d(VERT_FLOAT3, verts + a->offset, + geo->morphTargets[0].normals, + header->totalNumVertex, a->stride); + } + + // Prelighting + if(isPrelit){ + for(a = attribs; a->index != ATTRIB_COLOR; a++) + ; + instColor(VERT_RGBA, verts + a->offset, + geo->colors, + header->totalNumVertex, a->stride); + } + + // Texture coordinates + for(int32 n = 0; n < geo->numTexCoordSets; n++){ + for(a = attribs; a->index != ATTRIB_TEXCOORDS0+n; a++) + ; + instV2d(VERT_FLOAT2, verts + a->offset, + geo->texCoords[n], + header->totalNumVertex, a->stride); + } + + glGenBuffers(1, &header->vbo); + glBindBuffer(GL_ARRAY_BUFFER, header->vbo); + glBufferData(GL_ARRAY_BUFFER, header->totalNumVertex*stride, + header->vertexBuffer, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +void +defaultUninstanceCB(Geometry *geo, InstanceDataHeader *header) +{ + assert(0 && "can't uninstance"); +} + +ObjPipeline* +makeDefaultPipeline(void) +{ + ObjPipeline *pipe = new ObjPipeline(PLATFORM_GL3); + pipe->instanceCB = defaultInstanceCB; + pipe->uninstanceCB = defaultUninstanceCB; + pipe->renderCB = defaultRenderCB; + return pipe; +} + +ObjPipeline* +makeSkinPipeline(void) +{ + ObjPipeline *pipe = new ObjPipeline(PLATFORM_GL3); + pipe->instanceCB = defaultInstanceCB; + pipe->uninstanceCB = defaultUninstanceCB; + pipe->renderCB = defaultRenderCB; + pipe->pluginID = ID_SKIN; + pipe->pluginData = 1; + return pipe; +} + +ObjPipeline* +makeMatFXPipeline(void) +{ + ObjPipeline *pipe = new ObjPipeline(PLATFORM_GL3); + pipe->instanceCB = defaultInstanceCB; + pipe->uninstanceCB = defaultUninstanceCB; + pipe->renderCB = defaultRenderCB; + pipe->pluginID = ID_MATFX; + pipe->pluginData = 0; + return pipe; +} + +#endif + +} +} diff --git a/src/gl/gl3raster.cpp b/src/gl/gl3raster.cpp new file mode 100644 index 0000000..e698026 --- /dev/null +++ b/src/gl/gl3raster.cpp @@ -0,0 +1,132 @@ +#include +#include +#include +#include + +#include "../rwbase.h" +#include "../rwerror.h" +#include "../rwplg.h" +#include "../rwpipeline.h" +#include "../rwobjects.h" +#include "../rwengine.h" +#include "../rwplugins.h" +#ifdef RW_OPENGL +#include +#endif +#include "rwgl3.h" +#include "rwgl3shader.h" + +namespace rw { +namespace gl3 { + +int32 nativeRasterOffset; + +static void +rasterCreate(Raster *raster) +{ + Gl3Raster *natras = PLUGINOFFSET(Gl3Raster, raster, nativeRasterOffset); + if(raster->flags & Raster::DONTALLOCATE) + return; + + assert(raster->depth == 32); + +#ifdef RW_OPENGL + glGenTextures(1, &natras->texid); + glBindTexture(GL_TEXTURE_2D, natras->texid); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, raster->width, raster->height, + 0, GL_RGBA, GL_UNSIGNED_BYTE, nil); + + glBindTexture(GL_TEXTURE_2D, 0); +#endif +} + +static uint8* +rasterLock(Raster*, int32 level) +{ + printf("locking\n"); + return nil; +} + +static void +rasterUnlock(Raster*, int32) +{ + printf("unlocking\n"); +} + +static int32 +rasterNumLevels(Raster*) +{ + printf("numlevels\n"); + return 0; +} + +static void +rasterFromImage(Raster *raster, Image *image) +{ + int32 format; + Gl3Raster *natras = PLUGINOFFSET(Gl3Raster, raster, nativeRasterOffset); + + format = Raster::C8888; + format |= 4; + + raster->type = format & 0x7; + raster->flags = format & 0xF8; + raster->format = format & 0xFF00; + rasterCreate(raster); + + assert(image->depth == 32); + + natras->hasAlpha = image->hasAlpha(); + +#ifdef RW_OPENGL + glBindTexture(GL_TEXTURE_2D, natras->texid); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, raster->width, raster->height, + 0, GL_RGBA, GL_UNSIGNED_BYTE, image->pixels); + glBindTexture(GL_TEXTURE_2D, 0); +#endif +} + +static void* +createNativeRaster(void *object, int32 offset, int32) +{ + Gl3Raster *ras = PLUGINOFFSET(Gl3Raster, object, offset); + ras->texid = 0; + return object; +} + +static void* +destroyNativeRaster(void *object, int32, int32) +{ + //Gl3Raster *ras = PLUGINOFFSET(Gl3Raster, object, offset); + // TODO + return object; +} + +static void* +copyNativeRaster(void *dst, void *, int32 offset, int32) +{ + Gl3Raster *d = PLUGINOFFSET(Gl3Raster, dst, offset); + d->texid = 0; + return dst; +} + +void +registerNativeRaster(void) +{ + nativeRasterOffset = Raster::registerPlugin(sizeof(Gl3Raster), + 0x12340000 | PLATFORM_GL3, + createNativeRaster, + destroyNativeRaster, + copyNativeRaster); + driver[PLATFORM_GL3].rasterNativeOffset = nativeRasterOffset; + driver[PLATFORM_GL3].rasterCreate = rasterCreate; + driver[PLATFORM_GL3].rasterLock = rasterLock; + driver[PLATFORM_GL3].rasterUnlock = rasterUnlock; + driver[PLATFORM_GL3].rasterNumLevels = rasterNumLevels; + driver[PLATFORM_GL3].rasterFromImage = rasterFromImage; +} + +} +} diff --git a/src/gl/gl3render.cpp b/src/gl/gl3render.cpp new file mode 100644 index 0000000..af5ff66 --- /dev/null +++ b/src/gl/gl3render.cpp @@ -0,0 +1,359 @@ +#include +#include +#include + +#include "../rwbase.h" +#include "../rwerror.h" +#include "../rwplg.h" +#include "../rwpipeline.h" +#include "../rwobjects.h" +#include "../rwengine.h" +#include "../rwplugins.h" +#ifdef RW_OPENGL +#include +#include "rwgl3.h" +#include "rwgl3shader.h" + +namespace rw { +namespace gl3 { + +struct UniformScene +{ + float32 proj[16]; + float32 view[16]; +}; + +struct UniformLight +{ + V3d position; + float32 w; + V3d direction; + int32 pad1; + RGBAf color; + float32 radius; + float32 minusCosAngle; + int32 pad2[2]; +}; + +#define MAX_LIGHTS 8 + +struct UniformObject +{ + Matrix world; + RGBAf ambLight; + int32 numLights; + int32 pad[3]; + UniformLight lights[MAX_LIGHTS]; +}; + +GLuint vao; +GLuint ubo_scene, ubo_object; +GLuint whitetex; +UniformScene uniformScene; +UniformObject uniformObject; + +void +beginUpdate(Camera *cam) +{ + float view[16], proj[16]; + // View Matrix + Matrix inv; + Matrix::invert(&inv, cam->getFrame()->getLTM()); + // Since we're looking into positive Z, + // flip X to ge a left handed view space. + view[0] = -inv.right.x; + view[1] = inv.right.y; + view[2] = inv.right.z; + view[3] = 0.0f; + view[4] = -inv.up.x; + view[5] = inv.up.y; + view[6] = inv.up.z; + view[7] = 0.0f; + view[8] = -inv.at.x; + view[9] = inv.at.y; + view[10] = inv.at.z; + view[11] = 0.0f; + view[12] = -inv.pos.x; + view[13] = inv.pos.y; + view[14] = inv.pos.z; + view[15] = 1.0f; + setViewMatrix(view); + + // Projection Matrix + float32 invwx = 1.0f/cam->viewWindow.x; + float32 invwy = 1.0f/cam->viewWindow.y; + float32 invz = 1.0f/(cam->farPlane-cam->nearPlane); + + proj[0] = invwx; + proj[1] = 0.0f; + proj[2] = 0.0f; + proj[3] = 0.0f; + + proj[4] = 0.0f; + proj[5] = invwy; + proj[6] = 0.0f; + proj[7] = 0.0f; + + if(cam->projection == Camera::PERSPECTIVE){ + proj[8] = cam->viewOffset.x*invwx; + proj[9] = cam->viewOffset.y*invwy; + proj[10] = (cam->farPlane+cam->nearPlane)*invz; + proj[11] = 1.0f; + + proj[12] = 0.0f; + proj[13] = 0.0f; + proj[14] = -2.0f*cam->nearPlane*cam->farPlane*invz; + proj[15] = 0.0f; + }else{ + // TODO + } + setProjectionMatrix(proj); +} + +void +initializeRender(void) +{ + driver[PLATFORM_GL3].beginUpdate = beginUpdate; + + glClearColor(0.25, 0.25, 0.25, 1.0); + + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LESS); + + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glGenVertexArrays(1, &vao); + glBindVertexArray(vao); + + registerBlock("Scene"); + registerBlock("Object"); + registerUniform("u_matColor"); + registerUniform("u_surfaceProps"); + + glGenBuffers(1, &ubo_scene); + glBindBuffer(GL_UNIFORM_BUFFER, ubo_scene); + glBindBufferBase(GL_UNIFORM_BUFFER, gl3::findBlock("Scene"), ubo_scene); + glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformScene), &uniformScene, + GL_DYNAMIC_DRAW); + glBindBuffer(GL_UNIFORM_BUFFER, 0); + + + glGenBuffers(1, &ubo_object); + glBindBuffer(GL_UNIFORM_BUFFER, ubo_object); + glBindBufferBase(GL_UNIFORM_BUFFER, gl3::findBlock("Object"), ubo_object); + glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformObject), &uniformObject, + GL_DYNAMIC_DRAW); + glBindBuffer(GL_UNIFORM_BUFFER, 0); + + + byte whitepixel[4] = {0xFF, 0xFF, 0xFF, 0xFF}; + glGenTextures(1, &whitetex); + glBindTexture(GL_TEXTURE_2D, whitetex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, + 0, GL_RGBA, GL_UNSIGNED_BYTE, &whitepixel); +} + +void +setAttribPointers(InstanceDataHeader *header) +{ + AttribDesc *a; + for(a = header->attribDesc; + a != &header->attribDesc[header->numAttribs]; + a++){ + glEnableVertexAttribArray(a->index); + glVertexAttribPointer(a->index, a->size, a->type, a->normalized, + a->stride, (void*)(uint64)a->offset); + } +} + +static bool32 sceneDirty = 1; +static bool32 objectDirty = 1; + +void +setWorldMatrix(Matrix *mat) +{ + uniformObject.world = *mat; + objectDirty = 1; +} + +void +setAmbientLight(RGBAf *amb) +{ + uniformObject.ambLight = *amb; + objectDirty = 1; +} + +void +setNumLights(int32 n) +{ + uniformObject.numLights = n; + objectDirty = 1; +} + +void +setLight(int32 n, Light *light) +{ + UniformLight *l; + Frame *f; + Matrix *m; + + l = &uniformObject.lights[n]; + f = light->getFrame(); + if(f){ + m = f->getLTM(); + l->position = m->pos; + l->direction = m->at; + } + // light has position + l->w = light->getType() >= Light::POINT ? 1.0f : 0.0; + l->color = light->color; + l->radius = light->radius; + l->minusCosAngle = light->minusCosAngle; + objectDirty = 1; +} + +void +setProjectionMatrix(float32 *mat) +{ + memcpy(&uniformScene.proj, mat, 64); + sceneDirty = 1; +} + +void +setViewMatrix(float32 *mat) +{ + memcpy(&uniformScene.view, mat, 64); + sceneDirty = 1; +} + +static bool32 vertexAlpha; +static bool32 textureAlpha; + +void +setTexture(int32 n, Texture *tex) +{ + bool32 alpha; + glActiveTexture(GL_TEXTURE0+n); + if(tex == nil){ + glBindTexture(GL_TEXTURE_2D, whitetex); + alpha = 0; + }else{ + Gl3Raster *natras = PLUGINOFFSET(Gl3Raster, tex->raster, + nativeRasterOffset); + glBindTexture(GL_TEXTURE_2D, natras->texid); + alpha = natras->hasAlpha; + } + + if(textureAlpha == alpha) + return; + if(alpha) + /*printf("enable\n"),*/ glEnable(GL_BLEND); + else if(!vertexAlpha) + /*printf("disable\n"),*/ glDisable(GL_BLEND); + textureAlpha = alpha; +} + +void +setVertexAlpha(bool32 alpha) +{ + if(vertexAlpha == alpha) + return; + if(alpha) + /*printf("enable\n"),*/ glEnable(GL_BLEND); + else if(!textureAlpha) + /*printf("disable\n"),*/ glDisable(GL_BLEND); + vertexAlpha = alpha; +} + + +void +flushCache(void) +{ + if(objectDirty){ + glBindBuffer(GL_UNIFORM_BUFFER, ubo_object); + glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(UniformObject), + &uniformObject); + objectDirty = 0; + } + if(sceneDirty){ + glBindBuffer(GL_UNIFORM_BUFFER, ubo_scene); + glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(UniformScene), + &uniformScene); + sceneDirty = 0; + } +} + +void +lightingCB(void) +{ + World *world; + RGBAf ambLight = (RGBAf){0.0, 0.0, 0.0, 1.0}; + int n = 0; + + world = (World*)engine.currentWorld; + // only unpositioned lights right now + FORLIST(lnk, world->directionalLights){ + Light *l = Light::fromWorld(lnk); + if(l->getType() == Light::DIRECTIONAL){ + if(n >= MAX_LIGHTS) + continue; + setLight(n++, l); + }else if(l->getType() == Light::AMBIENT){ + ambLight.red += l->color.red; + ambLight.green += l->color.green; + ambLight.blue += l->color.blue; + } + } + setNumLights(n); + setAmbientLight(&ambLight); +} + +void +defaultRenderCB(Atomic *atomic, InstanceDataHeader *header) +{ + setWorldMatrix(atomic->getFrame()->getLTM()); + lightingCB(); + + glBindBuffer(GL_ARRAY_BUFFER, header->vbo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, header->ibo); + setAttribPointers(header); + + Material *m; + RGBAf col; + GLfloat surfProps[4]; + int id; + InstanceData *inst = header->inst; + int32 n = header->numMeshes; + + while(n--){ + m = inst->material; + +#define U(s) currentShader->uniformLocations[findUniform(s)] + + convColor(&col, &m->color); + glUniform4fv(U("u_matColor"), 1, (GLfloat*)&col); + + surfProps[0] = m->surfaceProps.ambient; + surfProps[1] = m->surfaceProps.specular; + surfProps[2] = m->surfaceProps.diffuse; + surfProps[3] = 0.0f; + glUniform4fv(U("u_surfaceProps"), 1, surfProps); + + setTexture(0, m->texture); + + setVertexAlpha(inst->vertexAlpha || m->color.alpha != 0xFF); + + flushCache(); + glDrawElements(header->primType, inst->numIndex, + GL_UNSIGNED_SHORT, (void*)(uintptr)inst->offset); + inst++; + } +} + + +} +} + +#endif diff --git a/src/gl/gl3shader.cpp b/src/gl/gl3shader.cpp new file mode 100644 index 0000000..afcba7d --- /dev/null +++ b/src/gl/gl3shader.cpp @@ -0,0 +1,193 @@ +#include +#include +#include + +#include "../rwbase.h" +#include "../rwerror.h" +#include "../rwplg.h" +#include "../rwpipeline.h" +#include "../rwobjects.h" +#include "../rwengine.h" +#include "../rwplugins.h" +#ifdef RW_OPENGL +#include +#include "rwgl3.h" +#include "rwgl3shader.h" + +namespace rw { +namespace gl3 { + +UniformRegistry uniformRegistry; + +int +registerUniform(const char *name) +{ + int i; + i = findUniform(name); + if(i >= 0) return i; + uniformRegistry.uniformNames[uniformRegistry.numUniforms] = strdup(name); + return uniformRegistry.numUniforms++; +} + +int +findUniform(const char *name) +{ + int i; + for(i = 0; i < uniformRegistry.numUniforms; i++) + if(strcmp(name, uniformRegistry.uniformNames[i]) == 0) + return i; + return -1; +} + +int +registerBlock(const char *name) +{ + int i; + i = findBlock(name); + if(i >= 0) return i; + uniformRegistry.blockNames[uniformRegistry.numBlocks] = strdup(name); + return uniformRegistry.numBlocks++; +} + +int +findBlock(const char *name) +{ + int i; + for(i = 0; i < uniformRegistry.numBlocks; i++) + if(strcmp(name, uniformRegistry.blockNames[i]) == 0) + return i; + return -1; +} + +Shader *currentShader; + +// TODO: maybe make this public somewhere? +static char* +loadfile(const char *path) +{ + FILE *f; + char *buf; + long len; + + if(f = fopen(path, "rb"), f == nil){ + fprintf(stderr, "Couldn't open file %s\n", path); + exit(1); + } + fseek(f, 0, SEEK_END); + len = ftell(f); + buf = (char*)malloc(len+1); + rewind(f); + fread(buf, 1, len, f); + buf[len] = '\0'; + fclose(f); + return buf; +} + +static int +compileshader(GLenum type, const char *src, GLuint *shader) +{ + GLint shdr, success; + GLint len; + char *log; + + shdr = glCreateShader(type); + glShaderSource(shdr, 1, &src, nil); + glCompileShader(shdr); + glGetShaderiv(shdr, GL_COMPILE_STATUS, &success); + if(!success){ + fprintf(stderr, "Error in %s shader\n", + type == GL_VERTEX_SHADER ? "vertex" : "fragment"); + glGetShaderiv(shdr, GL_INFO_LOG_LENGTH, &len); + log = (char*)malloc(len); + glGetShaderInfoLog(shdr, len, nil, log); + fprintf(stderr, "%s\n", log); + free(log); + return 1; + } + *shader = shdr; + return 0; +} + +static int +linkprogram(GLint vs, GLint fs, GLuint *program) +{ + GLint prog, success; + GLint len; + char *log; + + prog = glCreateProgram(); + + glAttachShader(prog, vs); + glAttachShader(prog, fs); + glLinkProgram(prog); + glGetProgramiv(prog, GL_LINK_STATUS, &success); + if(!success){ + fprintf(stderr, "Error in program\n"); + glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &len); + log = (char*)malloc(len); + glGetProgramInfoLog(prog, len, nil, log); + fprintf(stderr, "%s\n", log); + free(log); + return 1; + } + *program = prog; + return 0; +} + +Shader* +Shader::fromFiles(const char *vspath, const char *fspath) +{ + GLuint vs, fs, program; + int i; + char *src; + int fail; + + src = loadfile(vspath); + fail = compileshader(GL_VERTEX_SHADER, src, &vs); + free(src); + if(fail) + return nil; + + src = loadfile(fspath); + fail = compileshader(GL_FRAGMENT_SHADER, src, &fs); + free(src); + if(fail) + return nil; + + fail = linkprogram(vs, fs, &program); + if(fail) + return nil; + glDeleteProgram(vs); + glDeleteProgram(fs); + + Shader *sh = new Shader; + + // set uniform block binding + for(i = 0; i < uniformRegistry.numBlocks; i++){ + int idx = glGetUniformBlockIndex(program, + uniformRegistry.blockNames[i]); + if(idx >= 0) + glUniformBlockBinding(program, idx, i); + } + + // query uniform locations + sh->program = program; + sh->uniformLocations = new GLint[uniformRegistry.numUniforms]; + for(i = 0; i < uniformRegistry.numUniforms; i++) + sh->uniformLocations[i] = glGetUniformLocation(program, + uniformRegistry.uniformNames[i]); + + return sh; +} + +void +Shader::use(void) +{ + glUseProgram(this->program); + currentShader = this; +} + +} +} + +#endif diff --git a/src/gl/rwgl3.h b/src/gl/rwgl3.h new file mode 100644 index 0000000..27236da --- /dev/null +++ b/src/gl/rwgl3.h @@ -0,0 +1,108 @@ +namespace rw { +namespace gl3 { + +void initializePlatform(void); +void initializeRender(void); + +// arguments to glVertexAttribPointer basically +struct AttribDesc +{ + uint32 index; + int32 type; + bool32 normalized; + int32 size; + uint32 stride; + uint32 offset; +}; + +enum AttribIndices +{ + ATTRIB_POS = 0, + ATTRIB_NORMAL, + ATTRIB_COLOR, + ATTRIB_TEXCOORDS0, + ATTRIB_TEXCOORDS1, + ATTRIB_TEXCOORDS2, + ATTRIB_TEXCOORDS3, + ATTRIB_TEXCOORDS4, + ATTRIB_TEXCOORDS5, + ATTRIB_TEXCOORDS6, + ATTRIB_TEXCOORDS7, +}; + +struct InstanceData +{ + uint32 numIndex; + uint32 minVert; // not used for rendering + Material *material; + bool32 vertexAlpha; + uint32 program; + uint32 offset; +}; + +struct InstanceDataHeader : rw::InstanceDataHeader +{ + uint32 serialNumber; // not really needed right now + uint32 numMeshes; + uint16 *indexBuffer; + uint32 primType; + uint8 *vertexBuffer; + int32 numAttribs; + AttribDesc *attribDesc; + uint32 totalNumIndex; + uint32 totalNumVertex; + + uint32 ibo; + uint32 vbo; // or 2? + + InstanceData *inst; +}; + +void setAttribPointers(InstanceDataHeader *header); + +// per Scene +void setProjectionMatrix(float32*); +void setViewMatrix(float32*); + +// per Object +void setWorldMatrix(Matrix*); +void setAmbientLight(RGBAf*); +void setNumLights(int32 n); +void setLight(int32 n, Light*); + +// per Mesh +void setTexture(int32 n, Texture *tex); +void setVertexAlpha(bool32 enable); + +class ObjPipeline : public rw::ObjPipeline +{ +public: + void (*instanceCB)(Geometry *geo, InstanceDataHeader *header); + void (*uninstanceCB)(Geometry *geo, InstanceDataHeader *header); + void (*renderCB)(Atomic *atomic, InstanceDataHeader *header); + + ObjPipeline(uint32 platform); +}; + +void defaultInstanceCB(Geometry *geo, InstanceDataHeader *header); +void defaultUninstanceCB(Geometry *geo, InstanceDataHeader *header); +void defaultRenderCB(Atomic *atomic, InstanceDataHeader *header); + +ObjPipeline *makeDefaultPipeline(void); +ObjPipeline *makeSkinPipeline(void); +ObjPipeline *makeMatFXPipeline(void); + +// Native Texture and Raster + +extern int32 nativeRasterOffset; + +struct Gl3Raster +{ + uint32 texid; + bool32 hasAlpha; +}; + +void registerNativeRaster(void); + +} +} diff --git a/src/gl/rwgl3shader.h b/src/gl/rwgl3shader.h new file mode 100644 index 0000000..40f96b2 --- /dev/null +++ b/src/gl/rwgl3shader.h @@ -0,0 +1,44 @@ +#ifdef RW_OPENGL + +namespace rw { +namespace gl3 { + +// TODO: make this dynamic +enum { + MAX_UNIFORMS = 20, + MAX_BLOCKS = 20 +}; + +struct UniformRegistry +{ + int numUniforms; + char *uniformNames[MAX_UNIFORMS]; + + int numBlocks; + char *blockNames[MAX_BLOCKS]; +}; + +int registerUniform(const char *name); +int findUniform(const char *name); +int registerBlock(const char *name); +int findBlock(const char *name); + +extern UniformRegistry uniformRegistry; + +class Shader +{ +public: + GLuint program; + // same number of elements as UniformRegistry::numUniforms + GLint *uniformLocations; + + static Shader *fromFiles(const char *vs, const char *fs); + void use(void); +}; + +extern Shader *currentShader; + +} +} + +#endif diff --git a/src/gl/rwwdgl.h b/src/gl/rwwdgl.h new file mode 100644 index 0000000..badf959 --- /dev/null +++ b/src/gl/rwwdgl.h @@ -0,0 +1,83 @@ + +namespace rw { +namespace wdgl { + +// NOTE: This is not really RW OpenGL! It's specific to WarDrum's GTA ports + +void initializePlatform(void); + +struct AttribDesc +{ + // arguments to glVertexAttribPointer (should use OpenGL types here) + // Vertex = 0, TexCoord, Normal, Color, Weight, Bone Index, Extra Color + uint32 index; + // float = 0, byte, ubyte, short, ushort + int32 type; + bool32 normalized; + int32 size; + uint32 stride; + uint32 offset; +}; + +struct InstanceDataHeader : rw::InstanceDataHeader +{ + int32 numAttribs; + AttribDesc *attribs; + uint32 dataSize; + uint8 *data; + + // needed for rendering + uint32 vbo; + uint32 ibo; +}; + +// only RW_OPENGL +void uploadGeo(Geometry *geo); +void setAttribPointers(InstanceDataHeader *inst); + +void packattrib(uint8 *dst, float32 *src, AttribDesc *a, float32 scale); +void unpackattrib(float *dst, uint8 *src, AttribDesc *a, float32 scale); + +void *destroyNativeData(void *object, int32, int32); +Stream *readNativeData(Stream *stream, int32 len, void *object, int32, int32); +Stream *writeNativeData(Stream *stream, int32 len, void *object, int32, int32); +int32 getSizeNativeData(void *object, int32, int32); +void registerNativeDataPlugin(void); + +void printPipeinfo(Atomic *a); + +class ObjPipeline : public rw::ObjPipeline +{ +public: + uint32 numCustomAttribs; + uint32 (*instanceCB)(Geometry *g, int32 i, uint32 offset); + void (*uninstanceCB)(Geometry *g); + + ObjPipeline(uint32 platform); +}; + +ObjPipeline *makeDefaultPipeline(void); + +// Skin plugin + +Stream *readNativeSkin(Stream *stream, int32, void *object, int32 offset); +Stream *writeNativeSkin(Stream *stream, int32 len, void *object, int32 offset); +int32 getSizeNativeSkin(void *object, int32 offset); + +ObjPipeline *makeSkinPipeline(void); + +ObjPipeline *makeMatFXPipeline(void); + +// Raster + +struct Texture : rw::Texture +{ + void upload(void); + void bind(int n); +}; + +extern int32 nativeRasterOffset; +void registerNativeRaster(void); + +} +} diff --git a/src/gl/wdgl.cpp b/src/gl/wdgl.cpp new file mode 100644 index 0000000..3a496b1 --- /dev/null +++ b/src/gl/wdgl.cpp @@ -0,0 +1,793 @@ +#include +#include +#include +#include + +#include "../rwbase.h" +#include "../rwerror.h" +#include "../rwplg.h" +#include "../rwpipeline.h" +#include "../rwobjects.h" +#include "../rwengine.h" +#include "../rwplugins.h" +#include "rwwdgl.h" + +#ifdef RW_OPENGL +#include +#endif + +#define PLUGIN_ID 2 + +namespace rw { +namespace wdgl { + +void +initializePlatform(void) +{ + driver[PLATFORM_WDGL].defaultPipeline = makeDefaultPipeline(); +} + + +// VC +// 8733 0 0 0 3 +// 45 1 0 0 2 +// 8657 1 3 0 2 +// 4610 2 1 1 3 +// 4185 3 2 1 4 +// 256 4 2 1 4 +// 201 4 4 1 4 +// 457 5 2 0 4 + +// SA +// 20303 0 0 0 3 vertices: 3 floats +// 53 1 0 0 2 texCoords: 2 floats +// 20043 1 3 0 2 texCoords: 2 shorts +// 6954 2 1 1 3 normal: 3 bytes normalized +// 13527 3 2 1 4 color: 4 ubytes normalized +// 196 4 2 1 4 weight: 4 ubytes normalized +// 225 4 4 1 4 weight: 4 ushorts normalized +// 421 5 2 0 4 indices: 4 ubytes +// 12887 6 2 1 4 extracolor:4 ubytes normalized + +/* +static void +printAttribInfo(AttribDesc *attribs, int n) +{ + for(int i = 0; i < n; i++) + printf("%x %x %x %x\n", + attribs[i].index, + attribs[i].type, + attribs[i].normalized, + attribs[i].size); +} +*/ + +#ifdef RW_OPENGL +void +uploadGeo(Geometry *geo) +{ + InstanceDataHeader *inst = (InstanceDataHeader*)geo->instData; + MeshHeader *meshHeader = geo->meshHeader; + + glGenBuffers(1, &inst->vbo); + glBindBuffer(GL_ARRAY_BUFFER, inst->vbo); + glBufferData(GL_ARRAY_BUFFER, inst->dataSize, + inst->data, GL_STATIC_DRAW); + + glGenBuffers(1, &inst->ibo); + glBindBuffer(GL_ARRAY_BUFFER, inst->ibo); + glBufferData(GL_ARRAY_BUFFER, meshHeader->totalIndices*2, + 0, GL_STATIC_DRAW); + GLintptr offset = 0; + for(uint32 i = 0; i < meshHeader->numMeshes; i++){ + Mesh *mesh = &meshHeader->mesh[i]; + glBufferSubData(GL_ARRAY_BUFFER, offset, mesh->numIndices*2, + mesh->indices); + offset += mesh->numIndices*2; + } + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +void +setAttribPointers(InstanceDataHeader *inst) +{ + static GLenum attribType[] = { + GL_FLOAT, + GL_BYTE, GL_UNSIGNED_BYTE, + GL_SHORT, GL_UNSIGNED_SHORT + }; + for(int32 i = 0; i < inst->numAttribs; i++){ + AttribDesc *a = &inst->attribs[i]; + glEnableVertexAttribArray(a->index); + glVertexAttribPointer(a->index, a->size, attribType[a->type], + a->normalized, a->stride, + (void*)(uint64)a->offset); + } +} +#endif + +void +packattrib(uint8 *dst, float32 *src, AttribDesc *a, float32 scale=1.0f) +{ + int8 *i8dst; + uint16 *u16dst; + int16 *i16dst; + + switch(a->type){ + case 0: // float + memcpy(dst, src, a->size*4); + break; + + // TODO: maybe have loop inside if? + case 1: // byte + i8dst = (int8*)dst; + for(int i = 0; i < a->size; i++){ + if(!a->normalized) + i8dst[i] = src[i]*scale; + else + i8dst[i] = src[i]*127.0f; + } + break; + + case 2: // ubyte + for(int i = 0; i < a->size; i++){ + if(!a->normalized) + dst[i] = src[i]*scale; + else + dst[i] = src[i]*255.0f; + } + break; + + case 3: // short + i16dst = (int16*)dst; + for(int i = 0; i < a->size; i++){ + if(!a->normalized) + i16dst[i] = src[i]*scale; + else + i16dst[i] = src[i]*32767.0f; + } + break; + + case 4: // ushort + u16dst = (uint16*)dst; + for(int i = 0; i < a->size; i++){ + if(!a->normalized) + u16dst[i] = src[i]*scale; + else + u16dst[i] = src[i]*65535.0f; + } + break; + } +} + +void +unpackattrib(float *dst, uint8 *src, AttribDesc *a, float32 scale=1.0f) +{ + int8 *i8src; + uint16 *u16src; + int16 *i16src; + + switch(a->type){ + case 0: // float + memcpy(dst, src, a->size*4); + break; + + // TODO: maybe have loop inside if? + case 1: // byte + i8src = (int8*)src; + for(int i = 0; i < a->size; i++){ + if(!a->normalized) + dst[i] = i8src[i]/scale; + else + dst[i] = i8src[i]/127.0f; + } + break; + + case 2: // ubyte + for(int i = 0; i < a->size; i++){ + if(!a->normalized) + dst[i] = src[i]/scale; + else + dst[i] = src[i]/255.0f; + } + break; + + case 3: // short + i16src = (int16*)src; + for(int i = 0; i < a->size; i++){ + if(!a->normalized) + dst[i] = i16src[i]/scale; + else + dst[i] = i16src[i]/32767.0f; + } + break; + + case 4: // ushort + u16src = (uint16*)src; + for(int i = 0; i < a->size; i++){ + if(!a->normalized) + dst[i] = u16src[i]/scale; + else + dst[i] = u16src[i]/65435.0f; + } + break; + } +} + +void* +destroyNativeData(void *object, int32, int32) +{ + Geometry *geometry = (Geometry*)object; + if(geometry->instData == nil || + geometry->instData->platform != PLATFORM_WDGL) + return object; + InstanceDataHeader *header = + (InstanceDataHeader*)geometry->instData; + geometry->instData = nil; + // TODO: delete ibo and vbo + delete[] header->attribs; + delete[] header->data; + delete header; + return object; +} + +Stream* +readNativeData(Stream *stream, int32, void *object, int32, int32) +{ + Geometry *geometry = (Geometry*)object; + InstanceDataHeader *header = new InstanceDataHeader; + geometry->instData = header; + header->platform = PLATFORM_WDGL; + header->vbo = 0; + header->ibo = 0; + header->numAttribs = stream->readU32(); + header->attribs = new AttribDesc[header->numAttribs]; + stream->read(header->attribs, + header->numAttribs*sizeof(AttribDesc)); + header->dataSize = header->attribs[0].stride*geometry->numVertices; + header->data = new uint8[header->dataSize]; + stream->read(header->data, header->dataSize); + return stream; +} + +Stream* +writeNativeData(Stream *stream, int32, void *object, int32, int32) +{ + Geometry *geometry = (Geometry*)object; + if(geometry->instData == nil || + geometry->instData->platform != PLATFORM_WDGL) + return stream; + InstanceDataHeader *header = (InstanceDataHeader*)geometry->instData; + stream->writeU32(header->numAttribs); + stream->write(header->attribs, header->numAttribs*sizeof(AttribDesc)); + stream->write(header->data, header->dataSize); + return stream; +} + +int32 +getSizeNativeData(void *object, int32, int32) +{ + Geometry *geometry = (Geometry*)object; + if(geometry->instData == nil || + geometry->instData->platform != PLATFORM_WDGL) + return 0; + InstanceDataHeader *header = (InstanceDataHeader*)geometry->instData; + return 4 + header->numAttribs*sizeof(AttribDesc) + header->dataSize; +} + +void +registerNativeDataPlugin(void) +{ + Geometry::registerPlugin(0, ID_NATIVEDATA, + nil, destroyNativeData, nil); + Geometry::registerPluginStream(ID_NATIVEDATA, + readNativeData, + writeNativeData, + getSizeNativeData); +} + +void +printPipeinfo(Atomic *a) +{ + Geometry *g = a->geometry; + if(g->instData == nil || g->instData->platform != PLATFORM_WDGL) + return; + int32 plgid = 0; + if(a->pipeline) + plgid = a->pipeline->pluginID; + printf("%s %x: ", debugFile, plgid); + InstanceDataHeader *h = (InstanceDataHeader*)g->instData; + for(int i = 0; i < h->numAttribs; i++) + printf("%x(%x) ", h->attribs[i].index, h->attribs[i].type); + printf("\n"); +} + +static void +instance(rw::ObjPipeline *rwpipe, Atomic *atomic) +{ + ObjPipeline *pipe = (ObjPipeline*)rwpipe; + Geometry *geo = atomic->geometry; + if(geo->geoflags & Geometry::NATIVE) + return; + InstanceDataHeader *header = new InstanceDataHeader; + geo->instData = header; + header->platform = PLATFORM_WDGL; + header->vbo = 0; + header->ibo = 0; + header->numAttribs = + pipe->numCustomAttribs + 1 + (geo->numTexCoordSets > 0); + if(geo->geoflags & Geometry::PRELIT) + header->numAttribs++; + if(geo->geoflags & Geometry::NORMALS) + header->numAttribs++; + int32 offset = 0; + header->attribs = new AttribDesc[header->numAttribs]; + + AttribDesc *a = header->attribs; + // Vertices + a->index = 0; + a->type = 0; + a->normalized = 0; + a->size = 3; + a->offset = offset; + offset += 12; + a++; + int32 firstCustom = 1; + + // texCoords, only one set here + if(geo->numTexCoordSets){ + a->index = 1; + a->type = 3; + a->normalized = 0; + a->size = 2; + a->offset = offset; + offset += 4; + a++; + firstCustom++; + } + + if(geo->geoflags & Geometry::NORMALS){ + a->index = 2; + a->type = 1; + a->normalized = 1; + a->size = 3; + a->offset = offset; + offset += 4; + a++; + firstCustom++; + } + + if(geo->geoflags & Geometry::PRELIT){ + a->index = 3; + a->type = 2; + a->normalized = 1; + a->size = 4; + a->offset = offset; + offset += 4; + a++; + firstCustom++; + } + + if(pipe->instanceCB) + offset += pipe->instanceCB(geo, firstCustom, offset); + else{ + header->dataSize = offset*geo->numVertices; + header->data = new uint8[header->dataSize]; + } + + a = header->attribs; + for(int32 i = 0; i < header->numAttribs; i++) + a[i].stride = offset; + + uint8 *p = header->data + a->offset; + float32 *vert = geo->morphTargets->vertices; + for(int32 i = 0; i < geo->numVertices; i++){ + packattrib(p, vert, a); + vert += 3; + p += a->stride; + } + a++; + + if(geo->numTexCoordSets){ + p = header->data + a->offset; + float32 *texcoord = geo->texCoords[0]; + for(int32 i = 0; i < geo->numVertices; i++){ + packattrib(p, texcoord, a, 512.0f); + texcoord += 2; + p += a->stride; + } + a++; + } + + if(geo->geoflags & Geometry::NORMALS){ + p = header->data + a->offset; + float32 *norm = geo->morphTargets->normals; + for(int32 i = 0; i < geo->numVertices; i++){ + packattrib(p, norm, a); + norm += 3; + p += a->stride; + } + a++; + } + + if(geo->geoflags & Geometry::PRELIT){ + // TODO: this seems too complicated + p = header->data + a->offset; + uint8 *color = geo->colors; + float32 f[4]; + for(int32 i = 0; i < geo->numVertices; i++){ + f[0] = color[0]/255.0f; + f[1] = color[1]/255.0f; + f[2] = color[2]/255.0f; + f[3] = color[3]/255.0f; + packattrib(p, f, a); + color += 4; + p += a->stride; + } + a++; + } + geo->geoflags |= Geometry::NATIVE; +} + +static void +uninstance(rw::ObjPipeline *rwpipe, Atomic *atomic) +{ + ObjPipeline *pipe = (ObjPipeline*)rwpipe; + Geometry *geo = atomic->geometry; + if((geo->geoflags & Geometry::NATIVE) == 0) + return; + assert(geo->instData != nil); + assert(geo->instData->platform == PLATFORM_WDGL); + geo->geoflags &= ~Geometry::NATIVE; + geo->allocateData(); + + uint8 *p; + float32 *texcoord = geo->texCoords[0]; + uint8 *color = geo->colors; + float32 *vert = geo->morphTargets->vertices; + float32 *norm = geo->morphTargets->normals; + float32 f[4]; + + InstanceDataHeader *header = (InstanceDataHeader*)geo->instData; + for(int i = 0; i < header->numAttribs; i++){ + AttribDesc *a = &header->attribs[i]; + p = header->data + a->offset; + + switch(a->index){ + case 0: // Vertices + for(int32 i = 0; i < geo->numVertices; i++){ + unpackattrib(vert, p, a); + vert += 3; + p += a->stride; + } + break; + + case 1: // texCoords + for(int32 i = 0; i < geo->numVertices; i++){ + unpackattrib(texcoord, p, a, 512.0f); + texcoord += 2; + p += a->stride; + } + break; + + case 2: // normals + for(int32 i = 0; i < geo->numVertices; i++){ + unpackattrib(norm, p, a); + norm += 3; + p += a->stride; + } + break; + + case 3: // colors + for(int32 i = 0; i < geo->numVertices; i++){ + // TODO: this seems too complicated + unpackattrib(f, p, a); + color[0] = f[0]*255.0f; + color[1] = f[1]*255.0f; + color[2] = f[2]*255.0f; + color[3] = f[3]*255.0f; + color += 4; + p += a->stride; + } + break; + } + } + + if(pipe->uninstanceCB) + pipe->uninstanceCB(geo); + + geo->generateTriangles(); + + destroyNativeData(geo, 0, 0); +} + +ObjPipeline::ObjPipeline(uint32 platform) + : rw::ObjPipeline(platform) +{ + this->numCustomAttribs = 0; + this->impl.instance = wdgl::instance; + this->impl.uninstance = wdgl::uninstance; + this->instanceCB = nil; + this->uninstanceCB = nil; +} + +ObjPipeline* +makeDefaultPipeline(void) +{ + ObjPipeline *pipe = new ObjPipeline(PLATFORM_WDGL); + return pipe; +} + +// Skin + +Stream* +readNativeSkin(Stream *stream, int32, void *object, int32 offset) +{ + 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_GL){ + RWERROR((ERR_PLATFORM, platform)); + return nil; + } + Skin *skin = new Skin; + *PLUGINOFFSET(Skin*, geometry, offset) = skin; + + int32 numBones = stream->readI32(); + skin->init(numBones, 0, 0); + stream->read(skin->inverseMatrices, skin->numBones*64); + return stream; +} + +Stream* +writeNativeSkin(Stream *stream, int32 len, void *object, int32 offset) +{ + writeChunkHeader(stream, ID_STRUCT, len-12); + stream->writeU32(PLATFORM_WDGL); + Skin *skin = *PLUGINOFFSET(Skin*, object, offset); + stream->writeI32(skin->numBones); + stream->write(skin->inverseMatrices, skin->numBones*64); + return stream; +} + +int32 +getSizeNativeSkin(void *object, int32 offset) +{ + Skin *skin = *PLUGINOFFSET(Skin*, object, offset); + if(skin == nil) + return -1; + int32 size = 12 + 4 + 4 + skin->numBones*64; + return size; +} + +uint32 +skinInstanceCB(Geometry *g, int32 i, uint32 offset) +{ + InstanceDataHeader *header = (InstanceDataHeader*)g->instData; + AttribDesc *a = &header->attribs[i]; + // weights + a->index = 4; + a->type = 2; /* but also short o_O */ + a->normalized = 1; + a->size = 4; + a->offset = offset; + offset += 4; + a++; + + // indices + a->index = 5; + a->type = 2; + a->normalized = 0; + a->size = 4; + a->offset = offset; + offset += 4; + + header->dataSize = offset*g->numVertices; + header->data = new uint8[header->dataSize]; + + Skin *skin = *PLUGINOFFSET(Skin*, g, skinGlobals.offset); + if(skin == nil) + return 8; + + a = &header->attribs[i]; + uint8 *wgt = header->data + a[0].offset; + uint8 *idx = header->data + a[1].offset; + uint8 *indices = skin->indices; + float32 *weights = skin->weights; + for(int32 i = 0; i < g->numVertices; i++){ + packattrib(wgt, weights, a); + weights += 4; + wgt += offset; + idx[0] = *indices++; + idx[1] = *indices++; + idx[2] = *indices++; + idx[3] = *indices++; + idx += offset; + } + + return 8; +} + +void +skinUninstanceCB(Geometry *geo) +{ + InstanceDataHeader *header = (InstanceDataHeader*)geo->instData; + + Skin *skin = *PLUGINOFFSET(Skin*, geo, skinGlobals.offset); + if(skin == nil) + return; + + uint8 *data = skin->data; + float *invMats = skin->inverseMatrices; + skin->init(skin->numBones, skin->numBones, geo->numVertices); + memcpy(skin->inverseMatrices, invMats, skin->numBones*64); + delete[] data; + + uint8 *p; + float *weights = skin->weights; + uint8 *indices = skin->indices; + for(int i = 0; i < header->numAttribs; i++){ + AttribDesc *a = &header->attribs[i]; + p = header->data + a->offset; + + switch(a->index){ + case 4: // weights + for(int32 i = 0; i < geo->numVertices; i++){ + unpackattrib(weights, p, a); + weights += 4; + p += a->stride; + } + break; + + case 5: // indices + for(int32 i = 0; i < geo->numVertices; i++){ + *indices++ = p[0]; + *indices++ = p[1]; + *indices++ = p[2]; + *indices++ = p[3]; + p += a->stride; + } + break; + } + } + + skin->findNumWeights(geo->numVertices); + skin->findUsedBones(geo->numVertices); +} + +ObjPipeline* +makeSkinPipeline(void) +{ + ObjPipeline *pipe = new ObjPipeline(PLATFORM_WDGL); + pipe->pluginID = ID_SKIN; + pipe->pluginData = 1; + pipe->numCustomAttribs = 2; + pipe->instanceCB = skinInstanceCB; + pipe->uninstanceCB = skinUninstanceCB; + return pipe; +} + +ObjPipeline* +makeMatFXPipeline(void) +{ + ObjPipeline *pipe = new ObjPipeline(PLATFORM_WDGL); + pipe->pluginID = ID_MATFX; + pipe->pluginData = 0; + return pipe; +} + +// Raster + +int32 nativeRasterOffset; + +#ifdef RW_OPENGL +struct GlRaster { + GLuint id; +}; + +static void* +createNativeRaster(void *object, int32 offset, int32) +{ + GlRaster *raster = PLUGINOFFSET(GlRaster, object, offset); + raster->id = 0; + return object; +} + +static void* +destroyNativeRaster(void *object, int32 offset, int32) +{ + // TODO: + return object; +} + +static void* +copyNativeRaster(void *dst, void *, int32 offset, int32) +{ + GlRaster *raster = PLUGINOFFSET(GlRaster, dst, offset); + raster->id = 0; + return dst; +} + +void +registerNativeRaster(void) +{ + nativeRasterOffset = Raster::registerPlugin(sizeof(GlRaster), + 0x12340000 | PLATFORM_WDGL, + createNativeRaster, + destroyNativeRaster, + copyNativeRaster); +} + +void +Texture::upload(void) +{ + GLuint id; + glGenTextures(1, &id); + glBindTexture(GL_TEXTURE_2D, id); + Raster *r = this->raster; + if(r->palette){ + printf("can't upload paletted raster\n"); + return; + } + + static GLenum filter[] = { + 0, GL_NEAREST, GL_LINEAR, + GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_NEAREST, + GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_LINEAR + }; + static GLenum filternomip[] = { + 0, GL_NEAREST, GL_LINEAR, + GL_NEAREST, GL_LINEAR, + GL_NEAREST, GL_LINEAR + }; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, + filternomip[this->filterAddressing & 0xFF]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, + filternomip[this->filterAddressing & 0xFF]); + + static GLenum wrap[] = { + 0, GL_REPEAT, GL_MIRRORED_REPEAT, + GL_CLAMP_TO_EDGE, GL_CLAMP_TO_BORDER + }; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, + wrap[(this->filterAddressing >> 8) & 0xF]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, + wrap[(this->filterAddressing >> 12) & 0xF]); + + switch(r->format & 0xF00){ + case Raster::C8888: + glTexImage2D(GL_TEXTURE_2D, 0, 4, r->width, r->height, + 0, GL_RGBA, GL_UNSIGNED_BYTE, r->texels); + break; + default: + printf("unsupported raster format: %x\n", r->format); + break; + } + glBindTexture(GL_TEXTURE_2D, 0); + GlRaster *glr = PLUGINOFFSET(GlRaster, r, nativeRasterOffset); + glr->id = id; +} + +void +Texture::bind(int n) +{ + Raster *r = this->raster; + GlRaster *glr = PLUGINOFFSET(GlRaster, r, nativeRasterOffset); + glActiveTexture(GL_TEXTURE0+n); + if(r){ + if(glr->id == 0) + this->upload(); + glBindTexture(GL_TEXTURE_2D, glr->id); + }else + glBindTexture(GL_TEXTURE_2D, 0); + glActiveTexture(GL_TEXTURE0); + +} +#endif + +} +} diff --git a/src/image.cpp b/src/image.cpp old mode 100755 new mode 100644 diff --git a/src/ps2/pds.cpp b/src/ps2/pds.cpp new file mode 100644 index 0000000..a1596c1 --- /dev/null +++ b/src/ps2/pds.cpp @@ -0,0 +1,187 @@ +#include +#include +#include +#include + +#include "../rwbase.h" +#include "../rwplg.h" +#include "../rwpipeline.h" +#include "../rwobjects.h" +#include "../rwplugins.h" +#include "rwps2.h" +#include "rwps2plg.h" + +namespace rw { +namespace ps2 { + +struct PdsGlobals +{ + Pipeline **pipes; + int32 maxPipes; + int32 numPipes; +}; +static PdsGlobals pdsGlobals; + +Pipeline* +getPDSPipe(uint32 data) +{ + for(int32 i = 0; i < pdsGlobals.numPipes; i++) + if(pdsGlobals.pipes[i]->pluginData == data) + return pdsGlobals.pipes[i]; + return nil; +} + +void +registerPDSPipe(Pipeline *pipe) +{ + assert(pdsGlobals.numPipes < pdsGlobals.maxPipes); + pdsGlobals.pipes[pdsGlobals.numPipes++] = pipe; +} + +static void +atomicPDSRights(void *object, int32, int32, uint32 data) +{ + Atomic *a = (Atomic*)object; + a->pipeline = (ObjPipeline*)getPDSPipe(data); +// printf("atm pds: %x %x %x\n", data, a->pipeline->pluginID, a->pipeline->pluginData); +} + +static void +materialPDSRights(void *object, int32, int32, uint32 data) +{ + Material *m = (Material*)object; + m->pipeline = (ObjPipeline*)getPDSPipe(data); +// printf("mat pds: %x %x %x\n", data, m->pipeline->pluginID, m->pipeline->pluginData); +} + +void +registerPDSPlugin(int32 n) +{ + pdsGlobals.maxPipes = n; + pdsGlobals.numPipes = 0; + pdsGlobals.pipes = new Pipeline*[n]; + Atomic::registerPlugin(0, ID_PDS, nil, nil, nil); + Atomic::setStreamRightsCallback(ID_PDS, atomicPDSRights); + + Material::registerPlugin(0, ID_PDS, nil, nil, nil); + Material::setStreamRightsCallback(ID_PDS, materialPDSRights); +} + +void +registerPluginPDSPipes(void) +{ + // rwPDS_G3_Skin_GrpMatPipeID + MatPipeline *pipe = new MatPipeline(PLATFORM_PS2); + pipe->pluginID = ID_PDS; + pipe->pluginData = 0x11001; + pipe->attribs[AT_XYZ] = &attribXYZ; + pipe->attribs[AT_UV] = &attribUV; + pipe->attribs[AT_RGBA] = &attribRGBA; + pipe->attribs[AT_NORMAL] = &attribNormal; + pipe->attribs[AT_NORMAL+1] = &attribWeights; + uint32 vertCount = MatPipeline::getVertCount(VU_Lights-0x100, 5, 3, 2); + pipe->setTriBufferSizes(5, vertCount); + pipe->vifOffset = pipe->inputStride*vertCount; + pipe->instanceCB = skinInstanceCB; + pipe->uninstanceCB = genericUninstanceCB; + pipe->preUninstCB = skinPreCB; + pipe->postUninstCB = skinPostCB; + registerPDSPipe(pipe); + + // rwPDS_G3_Skin_GrpAtmPipeID + ObjPipeline *opipe = new ObjPipeline(PLATFORM_PS2); + opipe->pluginID = ID_PDS; + opipe->pluginData = 0x11002; + opipe->groupPipeline = pipe; + registerPDSPipe(opipe); + + // rwPDS_G3_MatfxUV1_GrpMatPipeID + pipe = new MatPipeline(PLATFORM_PS2); + pipe->pluginID = ID_PDS; + pipe->pluginData = 0x1100b; + pipe->attribs[AT_XYZ] = &attribXYZ; + pipe->attribs[AT_UV] = &attribUV; + pipe->attribs[AT_RGBA] = &attribRGBA; + pipe->attribs[AT_NORMAL] = &attribNormal; + vertCount = MatPipeline::getVertCount(0x3C5, 4, 3, 3); + pipe->setTriBufferSizes(4, vertCount); + pipe->vifOffset = pipe->inputStride*vertCount; + pipe->uninstanceCB = genericUninstanceCB; + registerPDSPipe(pipe); + + // rwPDS_G3_MatfxUV1_GrpAtmPipeID + opipe = new ObjPipeline(PLATFORM_PS2); + opipe->pluginID = ID_PDS; + opipe->pluginData = 0x1100d; + opipe->groupPipeline = pipe; + registerPDSPipe(opipe); + + // rwPDS_G3_MatfxUV2_GrpMatPipeID + pipe = new MatPipeline(PLATFORM_PS2); + pipe->pluginID = ID_PDS; + pipe->pluginData = 0x1100c; + pipe->attribs[AT_XYZ] = &attribXYZ; + pipe->attribs[AT_UV] = &attribUV2; + pipe->attribs[AT_RGBA] = &attribRGBA; + pipe->attribs[AT_NORMAL] = &attribNormal; + vertCount = MatPipeline::getVertCount(0x3C5, 4, 3, 3); + pipe->setTriBufferSizes(4, vertCount); + pipe->vifOffset = pipe->inputStride*vertCount; + pipe->uninstanceCB = genericUninstanceCB; + registerPDSPipe(pipe); + + // rwPDS_G3_MatfxUV2_GrpAtmPipeID + opipe = new ObjPipeline(PLATFORM_PS2); + opipe->pluginID = ID_PDS; + opipe->pluginData = 0x1100e; + opipe->groupPipeline = pipe; + registerPDSPipe(opipe); + + // RW World plugin + + // rwPDS_G3x_Generic_AtmPipeID + opipe = new ObjPipeline(PLATFORM_PS2); + opipe->pluginID = ID_PDS; + opipe->pluginData = 0x50001; + registerPDSPipe(opipe); + + // rwPDS_G3x_Skin_AtmPipeID + opipe = new ObjPipeline(PLATFORM_PS2); + opipe->pluginID = ID_PDS; + opipe->pluginData = 0x5000b; + registerPDSPipe(opipe); + + // rwPDS_G3xd_A4D_MatPipeID + pipe = new MatPipeline(PLATFORM_PS2); + pipe->pluginID = ID_PDS; + pipe->pluginData = 0x5002f; + pipe->attribs[0] = &attribXYZW; + pipe->attribs[1] = &attribUV; + pipe->attribs[2] = &attribNormal; + vertCount = 0x50; + pipe->setTriBufferSizes(3, vertCount); + pipe->vifOffset = pipe->inputStride*vertCount; + pipe->uninstanceCB = genericUninstanceCB; + pipe->preUninstCB = genericPreCB; + registerPDSPipe(pipe); + + // rwPDS_G3xd_A4DSkin_MatPipeID + pipe = new MatPipeline(PLATFORM_PS2); + pipe->pluginID = ID_PDS; + pipe->pluginData = 0x5003e; + pipe->attribs[0] = &attribXYZW; + pipe->attribs[1] = &attribUV; + pipe->attribs[2] = &attribNormal; + pipe->attribs[3] = &attribWeights; + vertCount = 0x30; + pipe->setTriBufferSizes(4, vertCount); + pipe->vifOffset = pipe->inputStride*vertCount; + pipe->instanceCB = skinInstanceCB; + pipe->uninstanceCB = genericUninstanceCB; + pipe->preUninstCB = genericPreCB; + pipe->postUninstCB = skinPostCB; + registerPDSPipe(pipe); +} + +} +} diff --git a/src/ps2/ps2.cpp b/src/ps2/ps2.cpp new file mode 100644 index 0000000..8798565 --- /dev/null +++ b/src/ps2/ps2.cpp @@ -0,0 +1,1627 @@ +#include +#include +#include +#include + +#include "../rwbase.h" +#include "../rwerror.h" +#include "../rwplg.h" +#include "../rwpipeline.h" +#include "../rwobjects.h" +#include "../rwengine.h" +#include "../rwplugins.h" +#include "rwps2.h" +#include "rwps2plg.h" + +#define PLUGIN_ID 2 + +namespace rw { +namespace ps2 { + +void +initializePlatform(void) +{ + driver[PLATFORM_PS2].defaultPipeline = makeDefaultPipeline(); +} + +ObjPipeline *defaultObjPipe; +MatPipeline *defaultMatPipe; + +void* +destroyNativeData(void *object, int32, int32) +{ + Geometry *geometry = (Geometry*)object; + if(geometry->instData == nil || + geometry->instData->platform != PLATFORM_PS2) + return object; + InstanceDataHeader *header = (InstanceDataHeader*)geometry->instData; + for(uint32 i = 0; i < header->numMeshes; i++) + delete[] header->instanceMeshes[i].data; + delete[] header->instanceMeshes; + delete header; + return object; +} + +Stream* +readNativeData(Stream *stream, int32, void *object, int32, int32) +{ + 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_PS2){ + RWERROR((ERR_PLATFORM, platform)); + return nil; + } + InstanceDataHeader *header = new InstanceDataHeader; + geometry->instData = header; + header->platform = PLATFORM_PS2; + assert(geometry->meshHeader != nil); + header->numMeshes = geometry->meshHeader->numMeshes; + header->instanceMeshes = new InstanceData[header->numMeshes]; + for(uint32 i = 0; i < header->numMeshes; i++){ + InstanceData *instance = &header->instanceMeshes[i]; + uint32 buf[2]; + stream->read(buf, 8); + instance->dataSize = buf[0]; + instance->arePointersFixed = buf[1]; +// TODO: force alignment + instance->data = new uint8[instance->dataSize]; +#ifdef RW_PS2 + uint32 a = (uint32)instance->data; + assert(a % 0x10 == 0); +#endif + stream->read(instance->data, instance->dataSize); + instance->material = geometry->meshHeader->mesh[i].material; +// sizedebug(instance); + } + return stream; +} + +Stream* +writeNativeData(Stream *stream, int32 len, void *object, int32, int32) +{ + Geometry *geometry = (Geometry*)object; + writeChunkHeader(stream, ID_STRUCT, len-12); + if(geometry->instData == nil || + geometry->instData->platform != PLATFORM_PS2) + return stream; + stream->writeU32(PLATFORM_PS2); + InstanceDataHeader *header = (InstanceDataHeader*)geometry->instData; + for(uint32 i = 0; i < header->numMeshes; i++){ + InstanceData *instance = &header->instanceMeshes[i]; + if(instance->arePointersFixed == 2) + unfixDmaOffsets(instance); + uint32 buf[2]; + buf[0] = instance->dataSize; + buf[1] = instance->arePointersFixed; + stream->write(buf, 8); + stream->write(instance->data, instance->dataSize); + } + return stream; +} + +int32 +getSizeNativeData(void *object, int32, int32) +{ + Geometry *geometry = (Geometry*)object; + int32 size = 16; + if(geometry->instData == nil || + geometry->instData->platform != PLATFORM_PS2) + return 0; + InstanceDataHeader *header = (InstanceDataHeader*)geometry->instData; + for(uint32 i = 0; i < header->numMeshes; i++){ + InstanceData *instance = &header->instanceMeshes[i]; + size += 8; + size += instance->dataSize; + } + return size; +} + +void +registerNativeDataPlugin(void) +{ + Geometry::registerPlugin(0, ID_NATIVEDATA, + nil, destroyNativeData, nil); + Geometry::registerPluginStream(ID_NATIVEDATA, + readNativeData, + writeNativeData, + getSizeNativeData); +} + +#ifdef RW_PS2 +void +fixDmaOffsets(InstanceData *inst) +{ + if(inst->arePointersFixed) + return; + + uint32 base = (uint32)inst->data; + uint32 *tag = (uint32*)inst->data; + for(;;){ + switch(tag[0]&0x70000000){ + // DMAcnt + case 0x10000000: + // no need to fix + tag += (1+(tag[0]&0xFFFF))*4; + break; + + // DMAref + case 0x30000000: + // fix address and jump to next + tag[1] = base + tag[1]<<4; + tag += 4; + break; + + // DMAret + case 0x60000000: + // we're done + inst->arePointersFixed = 2; + return; + + default: + fprintf(stderr, "error: unknown DMAtag %X\n", tag[0]); + return; + } + } +} +#endif + +void +unfixDmaOffsets(InstanceData *inst) +{ + (void)inst; +#ifdef RW_PS2 + if(inst->arePointersFixed != 2) + return; + + uint32 base = (uint32)inst->data; + uint32 *tag = (uint32*)inst->data; + for(;;){ + switch(tag[0]&0x70000000){ + // DMAcnt + case 0x10000000: + // no need to unfix + tag += (1+(tag[0]&0xFFFF))*4; + break; + + // DMAref + case 0x30000000: + // unfix address and jump to next + tag[1] = (tag[1] - base)>>4; + tag += 4; + break; + + // DMAret + case 0x60000000: + // we're done + inst->arePointersFixed = 0; + return; + + default: + fprintf(stderr, "error: unknown DMAtag %X\n", tag[0]); + return; + } + } +#endif +} + +// Pipeline + +PipeAttribute attribXYZ = { + "XYZ", + AT_V3_32 +}; + +PipeAttribute attribXYZW = { + "XYZW", + AT_V4_32 +}; + +PipeAttribute attribUV = { + "UV", + AT_V2_32 +}; + +PipeAttribute attribUV2 = { + "UV2", + AT_V4_32 +}; + +PipeAttribute attribRGBA = { + "RGBA", + AT_V4_8 | AT_UNSGN +}; + +PipeAttribute attribNormal = { + "Normal", + AT_V3_8 // RW has V4_8 but uses V3_8, wtf? +}; + +PipeAttribute attribWeights = { + "Weights", + AT_V4_32 | AT_RW +}; + +static uint32 +attribSize(uint32 unpack) +{ + static uint32 size[] = { 32, 16, 8, 16 }; + return ((unpack>>26 & 3)+1)*size[unpack>>24 & 3]/8; +} + +#define QWC(x) (((x)+0xF)>>4) + +static uint32 +getBatchSize(MatPipeline *pipe, uint32 vertCount) +{ + PipeAttribute *a; + if(vertCount == 0) + return 0; + uint32 size = 1; // ITOP &c. at the end + for(uint i = 0; i < nelem(pipe->attribs); i++) + if((a = pipe->attribs[i]) && (a->attrib & AT_RW) == 0){ + size++; // UNPACK &c. + size += QWC(vertCount*attribSize(a->attrib)); + } + return size; +} + +uint32* +instanceXYZ(uint32 *p, Geometry *g, Mesh *m, uint32 idx, uint32 n) +{ + uint16 j; + uint32 *d = (uint32*)g->morphTargets[0].vertices; + for(uint32 i = idx; i < idx+n; i++){ + j = m->indices[i]; + *p++ = d[j*3+0]; + *p++ = d[j*3+1]; + *p++ = d[j*3+2]; + } + while((uintptr)p % 0x10) + *p++ = 0; + return p; +} + +uint32* +instanceXYZW(uint32 *p, Geometry *g, Mesh *m, uint32 idx, uint32 n) +{ + uint16 j; + uint32 *d = (uint32*)g->morphTargets[0].vertices; + int8 *adcbits = getADCbitsForMesh(g, m); + for(uint32 i = idx; i < idx+n; i++){ + j = m->indices[i]; + *p++ = d[j*3+0]; + *p++ = d[j*3+1]; + *p++ = d[j*3+2]; + *p++ = adcbits && adcbits[i] ? 0x8000 : 0; + } + // don't need to pad + return p; +} + +uint32* +instanceUV(uint32 *p, Geometry *g, Mesh *m, uint32 idx, uint32 n) +{ + uint16 j; + uint32 *d = (uint32*)g->texCoords[0]; + if((g->geoflags & Geometry::TEXTURED) || + (g->geoflags & Geometry::TEXTURED2)) + for(uint32 i = idx; i < idx+n; i++){ + j = m->indices[i]; + *p++ = d[j*2+0]; + *p++ = d[j*2+1]; + } + else + for(uint32 i = idx; i < idx+n; i++){ + *p++ = 0; + *p++ = 0; + } + while((uintptr)p % 0x10) + *p++ = 0; + return p; +} + +uint32* +instanceUV2(uint32 *p, Geometry *g, Mesh *m, uint32 idx, uint32 n) +{ + uint16 j; + uint32 *d0 = (uint32*)g->texCoords[0]; + uint32 *d1 = (uint32*)g->texCoords[1]; + for(uint32 i = idx; i < idx+n; i++){ + j = m->indices[i]; + if(g->numTexCoordSets > 0){ + *p++ = d0[j*2+0]; + *p++ = d0[j*2+1]; + }else{ + *p++ = 0; + *p++ = 0; + } + if(g->numTexCoordSets > 1){ + *p++ = d1[j*2+0]; + *p++ = d1[j*2+1]; + }else{ + *p++ = 0; + *p++ = 0; + } + } + while((uintptr)p % 0x10) + *p++ = 0; + return p; +} + +uint32* +instanceRGBA(uint32 *p, Geometry *g, Mesh *m, uint32 idx, uint32 n) +{ + uint16 j; + uint32 *d = (uint32*)g->colors; + if((g->geoflags & Geometry::PRELIT)) + for(uint32 i = idx; i < idx+n; i++){ + j = m->indices[i]; + *p++ = d[j]; + } + else + for(uint32 i = idx; i < idx+n; i++) + *p++ = 0xFF000000; + while((uintptr)p % 0x10) + *p++ = 0; + return p; +} + +uint32* +instanceNormal(uint32 *wp, Geometry *g, Mesh *m, uint32 idx, uint32 n) +{ + uint16 j; + float *d = g->morphTargets[0].normals; + uint8 *p = (uint8*)wp; + if((g->geoflags & Geometry::NORMALS)) + for(uint32 i = idx; i < idx+n; i++){ + j = m->indices[i]; + *p++ = d[j*3+0]*127.0f; + *p++ = d[j*3+1]*127.0f; + *p++ = d[j*3+2]*127.0f; + } + else + for(uint32 i = idx; i < idx+n; i++){ + *p++ = 0; + *p++ = 0; + *p++ = 0; + } + while((uintptr)p % 0x10) + *p++ = 0; + return (uint32*)p; +} + +MatPipeline::MatPipeline(uint32 platform) + : rw::Pipeline(platform), instanceCB(nil), uninstanceCB(nil), + preUninstCB(nil), postUninstCB(nil) +{ + for(int i = 0; i < 10; i++) + this->attribs[i] = nil; +} + +void +MatPipeline::dump(void) +{ + if(this->platform != PLATFORM_PS2) + return; + PipeAttribute *a; + printf("%x %x\n", this->pluginID, this->pluginData); + for(uint i = 0; i < nelem(this->attribs); i++){ + a = this->attribs[i]; + if(a) + printf("%d %s: %x\n", i, a->name, a->attrib); + } + printf("stride: %x\n", this->inputStride); + printf("vertcount: %x\n", this->vifOffset/this->inputStride); + printf("triSCount: %x\n", this->triStripCount); + printf("triLCount: %x\n", this->triListCount); + printf("vifOffset: %x\n", this->vifOffset); + printf("\n"); +} + +void +MatPipeline::setTriBufferSizes(uint32 inputStride, uint32 stripCount) +{ + this->inputStride = inputStride; + this->triListCount = stripCount/12*12; + PipeAttribute *a; + for(uint i = 0; i < nelem(this->attribs); i++){ + a = this->attribs[i]; + if(a && a->attrib & AT_RW) + goto brokenout; + } + this->triStripCount = stripCount/4*4; + return; +brokenout: + this->triStripCount = (stripCount-2)/4*4+2; +} + +// Instance format: +// no broken out clusters +// ====================== +// DMAret [FLUSH; MSKPATH3 || FLUSH; FLUSH] { +// foreach batch { +// foreach cluster { +// MARK/0; STMOD; STCYCL; UNPACK +// unpack-data +// } +// ITOP; MSCALF/MSCNT; // if first/not-first +// 0/FLUSH; 0/MSKPATH3 || 0/FLUSH; 0/FLUSH // if not-last/last +// } +// } +// +// broken out clusters +// =================== +// foreach batch { +// foreach broken out cluster { +// DMAref [STCYCL; UNPACK] -> pointer into unpack-data +// DMAcnt (empty) +// } +// DMAcnt/ret { +// foreach cluster { +// MARK/0; STMOD; STCYCL; UNPACK +// unpack-data +// } +// ITOP; MSCALF/MSCNT; // if first/not-first +// 0/FLUSH; 0/MSKPATH3 || 0/FLUSH; 0/FLUSH // if not-last/last +// } +// } +// unpack-data for broken out clusters + +uint32 markcnt = 0; + +enum { + DMAcnt = 0x10000000, + DMAref = 0x30000000, + DMAret = 0x60000000, + + VIF_NOP = 0, + VIF_STCYCL = 0x01000100, // WL = 1 + VIF_ITOP = 0x04000000, + VIF_STMOD = 0x05000000, + VIF_MSKPATH3 = 0x06000000, + VIF_MARK = 0x07000000, + VIF_FLUSH = 0x11000000, + VIF_MSCALF = 0x15000000, + VIF_MSCNT = 0x17000000, +}; + +struct InstMeshInfo +{ + uint32 numAttribs, numBrokenAttribs; + uint32 batchVertCount, lastBatchVertCount; + uint32 numBatches; + uint32 batchSize, lastBatchSize; + uint32 size; // size of DMA chain without broken out data + uint32 size2; // size of broken out data + uint32 vertexSize; + uint32 attribPos[10]; +}; + +InstMeshInfo +getInstMeshInfo(MatPipeline *pipe, Geometry *g, Mesh *m) +{ + PipeAttribute *a; + InstMeshInfo im; + im.numAttribs = 0; + im.numBrokenAttribs = 0; + im.vertexSize = 0; + for(uint i = 0; i < nelem(pipe->attribs); i++) + if(a = pipe->attribs[i]) + if(a->attrib & AT_RW) + im.numBrokenAttribs++; + else{ + im.vertexSize += attribSize(a->attrib); + im.numAttribs++; + } + if(g->meshHeader->flags == MeshHeader::TRISTRIP){ + im.numBatches = (m->numIndices-2) / (pipe->triStripCount-2); + im.batchVertCount = pipe->triStripCount; + im.lastBatchVertCount = (m->numIndices-2) % (pipe->triStripCount-2); + if(im.lastBatchVertCount){ + im.numBatches++; + im.lastBatchVertCount += 2; + } + }else{ // TRILIST; nothing else supported yet + im.numBatches = (m->numIndices+pipe->triListCount-1) / + pipe->triListCount; + im.batchVertCount = pipe->triListCount; + im.lastBatchVertCount = m->numIndices % pipe->triListCount; + } + if(im.lastBatchVertCount == 0) + im.lastBatchVertCount = im.batchVertCount; + + im.batchSize = getBatchSize(pipe, im.batchVertCount); + im.lastBatchSize = getBatchSize(pipe, im.lastBatchVertCount); + if(im.numBrokenAttribs == 0) + im.size = 1 + im.batchSize*(im.numBatches-1) + im.lastBatchSize; + else + im.size = 2*im.numBrokenAttribs*im.numBatches + + (1+im.batchSize)*(im.numBatches-1) + 1+im.lastBatchSize; + + /* figure out size and addresses of broken out sections */ + im.size2 = 0; + for(uint i = 0; i < nelem(im.attribPos); i++) + if((a = pipe->attribs[i]) && a->attrib & AT_RW){ + im.attribPos[i] = im.size2 + im.size; + im.size2 += QWC(m->numIndices*attribSize(a->attrib)); + } + + return im; +} + +void +MatPipeline::instance(Geometry *g, InstanceData *inst, Mesh *m) +{ + PipeAttribute *a; + InstMeshInfo im = getInstMeshInfo(this, g, m); + + inst->dataSize = (im.size+im.size2)<<4; + inst->arePointersFixed = im.numBrokenAttribs == 0; + // TODO: force alignment + inst->data = new uint8[inst->dataSize]; + + /* make array of addresses of broken out sections */ + uint8 *datap[nelem(this->attribs)]; + uint8 **dp = datap; + for(uint i = 0; i < nelem(this->attribs); i++) + if((a = this->attribs[i]) && a->attrib & AT_RW) + dp[i] = inst->data + im.attribPos[i]*0x10; + + // TODO: not sure if this is correct + uint32 msk_flush = rw::version >= 0x35000 ? VIF_FLUSH : VIF_MSKPATH3; + + uint32 idx = 0; + uint32 *p = (uint32*)inst->data; + if(im.numBrokenAttribs == 0){ + *p++ = DMAret | im.size-1; + *p++ = 0; + *p++ = VIF_FLUSH; + *p++ = msk_flush; + } + for(uint32 j = 0; j < im.numBatches; j++){ + uint32 nverts, bsize; + if(j < im.numBatches-1){ + bsize = im.batchSize; + nverts = im.batchVertCount; + }else{ + bsize = im.lastBatchSize; + nverts = im.lastBatchVertCount; + } + for(uint i = 0; i < nelem(this->attribs); i++) + if((a = this->attribs[i]) && a->attrib & AT_RW){ + uint32 atsz = attribSize(a->attrib); + *p++ = DMAref | QWC(nverts*atsz); + *p++ = im.attribPos[i]; + *p++ = VIF_STCYCL | this->inputStride; + // Round up nverts so UNPACK will fit exactly into the DMA packet + // (can't pad with zeroes in broken out sections). + // TODO: check for clash with vifOffset somewhere + *p++ = (a->attrib&0xFF004000) + | 0x8000 | (QWC(nverts*atsz)<<4)/atsz << 16 | i; // UNPACK + + *p++ = DMAcnt; + *p++ = 0x0; + *p++ = VIF_NOP; + *p++ = VIF_NOP; + + im.attribPos[i] += g->meshHeader->flags == 1 ? + QWC((im.batchVertCount-2)*atsz) : + QWC(im.batchVertCount*atsz); + } + if(im.numBrokenAttribs){ + *p++ = (j < im.numBatches-1 ? DMAcnt : DMAret) | bsize; + *p++ = 0x0; + *p++ = VIF_NOP; + *p++ = VIF_NOP; + } + + for(uint i = 0; i < nelem(this->attribs); i++) + if((a = this->attribs[i]) && (a->attrib & AT_RW) == 0){ + if(rw::version >= 0x35000) + *p++ = VIF_NOP; + else + *p++ = VIF_MARK | markcnt++; + *p++ = VIF_STMOD; + *p++ = VIF_STCYCL | this->inputStride; + *p++ = (a->attrib&0xFF004000) + | 0x8000 | nverts << 16 | i; // UNPACK + + if(a == &attribXYZ) + p = instanceXYZ(p, g, m, idx, nverts); + else if(a == &attribXYZW) + p = instanceXYZW(p, g, m, idx, nverts); + else if(a == &attribUV) + p = instanceUV(p, g, m, idx, nverts); + else if(a == &attribUV2) + p = instanceUV2(p, g, m, idx, nverts); + else if(a == &attribRGBA) + p = instanceRGBA(p, g, m, idx, nverts); + else if(a == &attribNormal) + p = instanceNormal(p, g, m, idx, nverts); + } + idx += g->meshHeader->flags == 1 + ? im.batchVertCount-2 : im.batchVertCount; + + *p++ = VIF_ITOP | nverts; + *p++ = j == 0 ? VIF_MSCALF : VIF_MSCNT; + if(j < im.numBatches-1){ + *p++ = VIF_NOP; + *p++ = VIF_NOP; + }else{ + *p++ = VIF_FLUSH; + *p++ = msk_flush; + } + } + + if(this->instanceCB) + this->instanceCB(this, g, m, datap); +} + +uint8* +MatPipeline::collectData(Geometry *g, InstanceData *inst, Mesh *m, uint8 *data[]) +{ + PipeAttribute *a; + InstMeshInfo im = getInstMeshInfo(this, g, m); + + uint8 *raw = im.vertexSize*m->numIndices ? + new uint8[im.vertexSize*m->numIndices] : nil; + uint8 *dp = raw; + for(uint i = 0; i < nelem(this->attribs); i++) + if(a = this->attribs[i]) + if(a->attrib & AT_RW){ + data[i] = inst->data + im.attribPos[i]*0x10; + }else{ + data[i] = dp; + dp += m->numIndices*attribSize(a->attrib); + } + + uint8 *datap[nelem(this->attribs)]; + memcpy(datap, data, sizeof(datap)); + + uint32 overlap = g->meshHeader->flags == 1 ? 2 : 0; + uint32 *p = (uint32*)inst->data; + if(im.numBrokenAttribs == 0) + p += 4; + for(uint32 j = 0; j < im.numBatches; j++){ + uint32 nverts = j < im.numBatches-1 ? im.batchVertCount : + im.lastBatchVertCount; + for(uint i = 0; i < nelem(this->attribs); i++) + if((a = this->attribs[i]) && a->attrib & AT_RW) + p += 8; + if(im.numBrokenAttribs) + p += 4; + for(uint i = 0; i < nelem(this->attribs); i++) + if((a = this->attribs[i]) && (a->attrib & AT_RW) == 0){ + uint32 asz = attribSize(a->attrib); + p += 4; + if((p[-1] & 0xff004000) != a->attrib){ + fprintf(stderr, "unexpected unpack: %08x %08x\n", p[-1], a->attrib); + assert(0 && "unexpected unpack\n"); + } + memcpy(datap[i], p, asz*nverts); + datap[i] += asz*(nverts-overlap); + p += QWC(asz*nverts)*4; + } + p += 4; + } + return raw; +} + +static void +objInstance(rw::ObjPipeline *rwpipe, Atomic *atomic) +{ + ObjPipeline *pipe = (ObjPipeline*)rwpipe; + Geometry *geo = atomic->geometry; + if(geo->geoflags & Geometry::NATIVE) + return; + InstanceDataHeader *header = new InstanceDataHeader; + geo->instData = header; + header->platform = PLATFORM_PS2; + assert(geo->meshHeader != nil); + header->numMeshes = geo->meshHeader->numMeshes; + header->instanceMeshes = new InstanceData[header->numMeshes]; + for(uint32 i = 0; i < header->numMeshes; i++){ + Mesh *mesh = &geo->meshHeader->mesh[i]; + InstanceData *instance = &header->instanceMeshes[i]; + + MatPipeline *m; + m = pipe->groupPipeline ? + pipe->groupPipeline : + (MatPipeline*)mesh->material->pipeline; + if(m == nil) + m = defaultMatPipe; + m->instance(geo, instance, mesh); + instance->material = mesh->material; + } + geo->geoflags |= Geometry::NATIVE; +} + +static void +printVertCounts(InstanceData *inst, int flag) +{ + uint32 *d = (uint32*)inst->data; + uint32 id = 0; + if(inst->material->pipeline) + id = inst->material->pipeline->pluginData; + int stride; + if(inst->arePointersFixed){ + d += 4; + while(d[3]&0x60000000){ // skip UNPACKs + stride = d[2]&0xFF; + d += 4 + 4*QWC(attribSize(d[3])*((d[3]>>16)&0xFF)); + } + if(d[2] == 0) + printf("ITOP %x %d (%d) %x\n", *d, stride, flag, id); + }else{ + while((*d&0x70000000) == 0x30000000){ + stride = d[2]&0xFF; + printf("UNPACK %x %d (%d) %x\n", d[3], stride, flag, id); + d += 8; + } + if((*d&0x70000000) == 0x10000000){ + d += (*d&0xFFFF)*4; + printf("ITOP %x %d (%d) %x\n", *d, stride, flag, id); + } + } +} + +static void +objUninstance(rw::ObjPipeline *rwpipe, Atomic *atomic) +{ + ObjPipeline *pipe = (ObjPipeline*)rwpipe; + Geometry *geo = atomic->geometry; + if((geo->geoflags & Geometry::NATIVE) == 0) + return; + assert(geo->instData != nil); + assert(geo->instData->platform == PLATFORM_PS2); + InstanceDataHeader *header = (InstanceDataHeader*)geo->instData; + // highest possible number of vertices + geo->numVertices = geo->meshHeader->totalIndices; + geo->geoflags &= ~Geometry::NATIVE; + geo->allocateData(); + geo->meshHeader->allocateIndices(); + uint32 *flags = new uint32[geo->numVertices]; + memset(flags, 0, 4*geo->numVertices); + memset(geo->meshHeader->mesh[0].indices, 0, 2*geo->meshHeader->totalIndices); + for(uint32 i = 0; i < header->numMeshes; i++){ + Mesh *mesh = &geo->meshHeader->mesh[i]; + MatPipeline *m; + m = pipe->groupPipeline ? + pipe->groupPipeline : + (MatPipeline*)mesh->material->pipeline; + if(m == nil) m = defaultMatPipe; + if(m->preUninstCB) m->preUninstCB(m, geo); + } + geo->numVertices = 0; + for(uint32 i = 0; i < header->numMeshes; i++){ + Mesh *mesh = &geo->meshHeader->mesh[i]; + InstanceData *instance = &header->instanceMeshes[i]; + MatPipeline *m; + m = pipe->groupPipeline ? + pipe->groupPipeline : + (MatPipeline*)mesh->material->pipeline; + if(m == nil) m = defaultMatPipe; + + uint8 *data[nelem(m->attribs)] = { nil }; + uint8 *raw = m->collectData(geo, instance, mesh, data); + assert(m->uninstanceCB); + m->uninstanceCB(m, geo, flags, mesh, data); + if(raw) delete[] raw; + } + for(uint32 i = 0; i < header->numMeshes; i++){ + Mesh *mesh = &geo->meshHeader->mesh[i]; + MatPipeline *m; + m = pipe->groupPipeline ? + pipe->groupPipeline : + (MatPipeline*)mesh->material->pipeline; + if(m == nil) m = defaultMatPipe; + if(m->postUninstCB) m->postUninstCB(m, geo); + } + + int8 *bits = getADCbits(geo); + geo->generateTriangles(bits); + delete[] flags; + destroyNativeData(geo, 0, 0); + geo->instData = nil; +/* + for(uint32 i = 0; i < header->numMeshes; i++){ + Mesh *mesh = &geo->meshHeader->mesh[i]; + InstanceData *instance = &header->instanceMeshes[i]; +// printf("numIndices: %d\n", mesh->numIndices); +// printDMA(instance); + printVertCounts(instance, geo->meshHeader->flags); + } +*/ +} + +ObjPipeline::ObjPipeline(uint32 platform) + : rw::ObjPipeline(platform) +{ + this->groupPipeline = nil; + this->impl.instance = objInstance; + this->impl.uninstance = objUninstance; +} + +/* +int32 +findVertex(Geometry *g, uint32 flags[], uint32 mask, Vertex *v) +{ + float32 *verts = g->morphTargets[0].vertices; + float32 *tex = g->texCoords[0]; + float32 *tex1 = g->texCoords[1]; + float32 *norms = g->morphTargets[0].normals; + uint8 *cols = g->colors; + + for(int32 i = 0; i < g->numVertices; i++){ + if(mask & flags[i] & 0x1 && + !(verts[0] == v->p[0] && verts[1] == v->p[1] && verts[2] == v->p[2])) + goto cont; + if(mask & flags[i] & 0x10 && + !(norms[0] == v->n[0] && norms[1] == v->n[1] && norms[2] == v->n[2])) + goto cont; + if(mask & flags[i] & 0x100 && + !(cols[0] == v->c[0] && cols[1] == v->c[1] && cols[2] == v->c[2] && cols[3] == v->c[3])) + goto cont; + if(mask & flags[i] & 0x1000 && + !(tex[0] == v->t[0] && tex[1] == v->t[1])) + goto cont; + if(mask & flags[i] & 0x2000 && + !(tex1[0] == v->t1[0] && tex1[1] == v->t1[1])) + goto cont; + return i; + cont: + verts += 3; + tex += 2; + tex1 += 2; + norms += 3; + cols += 4; + } + return -1; +} +*/ + +void +insertVertex(Geometry *geo, int32 i, uint32 mask, Vertex *v) +{ + if(mask & 0x1) + memcpy(&geo->morphTargets[0].vertices[i*3], v->p, 12); + if(mask & 0x10) + memcpy(&geo->morphTargets[0].normals[i*3], v->n, 12); + if(mask & 0x100) + memcpy(&geo->colors[i*4], v->c, 4); + if(mask & 0x1000) + memcpy(&geo->texCoords[0][i*2], v->t, 8); + if(mask & 0x2000) + memcpy(&geo->texCoords[1][i*2], v->t1, 8); +} + +void +genericPreCB(MatPipeline *pipe, Geometry *geo) +{ + PipeAttribute *a; + for(int32 i = 0; i < nelem(pipe->attribs); i++) + if(a = pipe->attribs[i]) + if(a == &attribXYZW){ + allocateADC(geo); + break; + } + skinPreCB(pipe, geo); +} + +void +genericUninstanceCB(MatPipeline *pipe, Geometry *geo, uint32 flags[], Mesh *mesh, uint8 *data[]) +{ + float32 *xyz = nil, *xyzw = nil; + float32 *uv = nil, *uv2 = nil; + uint8 *rgba = nil; + int8 *normals = nil; + uint32 *weights = nil; + int8 *adc = nil; + Skin *skin = nil; + if(skinGlobals.offset) + skin = *PLUGINOFFSET(Skin*, geo, skinGlobals.offset); + + PipeAttribute *a; + for(int32 i = 0; i < nelem(pipe->attribs); i++) + if(a = pipe->attribs[i]){ + if(a == &attribXYZ) xyz = (float32*)data[i]; + else if(a == &attribXYZW) xyzw = (float32*)data[i]; + else if(a == &attribUV) uv = (float32*)data[i]; + else if(a == &attribUV2) uv2 = (float32*)data[i]; + else if(a == &attribRGBA) rgba = data[i]; + else if(a == &attribNormal) normals = (int8*)data[i]; + else if(a == &attribWeights) weights = (uint32*)data[i]; + } + + uint32 mask = 0x1; // vertices + if(normals && geo->geoflags & Geometry::NORMALS) + mask |= 0x10; + if(rgba && geo->geoflags & Geometry::PRELIT) + mask |= 0x100; + if((uv || uv2) && geo->numTexCoordSets > 0) + mask |= 0x1000; + if(uv2 && geo->numTexCoordSets > 1) + mask |= 0x2000; + if(weights && skin) + mask |= 0x10000; + if(xyzw) + adc = getADCbitsForMesh(geo, mesh); + + Vertex v; + for(uint32 i = 0; i < mesh->numIndices; i++){ + if(mask & 0x1) + memcpy(&v.p, xyz ? xyz : xyzw, 12); + if(mask & 0x10){ + // TODO: figure out scaling :/ + v.n[0] = normals[0]/128.0f; + v.n[1] = normals[1]/128.0f; + v.n[2] = normals[2]/128.0f; + } + if(mask & 0x100) + memcpy(&v.c, rgba, 4); + if(mask & 0x1000) + memcpy(&v.t, uv ? uv : uv2, 8); + if(mask & 0x2000) + memcpy(&v.t1, uv2 + 2, 8); + if(mask & 0x10000) + for(int j = 0; j < 4; j++){ + ((uint32*)v.w)[j] = weights[j] & ~0x3FF; + v.i[j] = (weights[j] & 0x3FF) >> 2; + if(v.i[j]) v.i[j]--; + if(v.w[j] == 0.0f) v.i[j] = 0; + } + int32 idx = findVertexSkin(geo, flags, mask, &v); + if(idx < 0) + idx = geo->numVertices++; + mesh->indices[i] = idx; + if(adc) + adc[i] = xyzw[3] != 0.0f; + flags[idx] = mask; + insertVertexSkin(geo, idx, mask, &v); + if(xyz) xyz += 3; + if(xyzw) xyzw += 4; + if(uv) uv += 2; + if(uv2) uv2 += 4; + rgba += 4; + normals += 3; + weights += 4; + } +} + +/* +void +defaultUninstanceCB(MatPipeline *pipe, Geometry *geo, uint32 flags[], Mesh *mesh, uint8 *data[]) +{ + float32 *verts = (float32*)data[AT_XYZ]; + float32 *texcoords = (float32*)data[AT_UV]; + uint8 *colors = (uint8*)data[AT_RGBA]; + int8 *norms = (int8*)data[AT_NORMAL]; + uint32 mask = 0x1; // vertices + if(geo->geoflags & Geometry::NORMALS) + mask |= 0x10; + if(geo->geoflags & Geometry::PRELIT) + mask |= 0x100; + for(int32 i = 0; i < geo->numTexCoordSets; i++) + mask |= 0x1000 << i; + int numUV = pipe->attribs[AT_UV] == &attribUV2 ? 2 : 1; + + Vertex v; + for(uint32 i = 0; i < mesh->numIndices; i++){ + if(mask & 0x1) + memcpy(&v.p, verts, 12); + if(mask & 0x10){ + v.n[0] = norms[0]/127.0f; + v.n[1] = norms[1]/127.0f; + v.n[2] = norms[2]/127.0f; + } + if(mask & 0x100){ + memcpy(&v.c, colors, 4); + //v.c[3] = 0xFF; + } + if(mask & 0x1000) + memcpy(&v.t, texcoords, 8); + if(mask & 0x2000) + memcpy(&v.t1, texcoords+2, 8); + + int32 idx = findVertex(geo, flags, mask, &v); + if(idx < 0) + idx = geo->numVertices++; + mesh->indices[i] = idx; + flags[idx] = mask; + insertVertex(geo, idx, mask, &v); + verts += 3; + texcoords += 2*numUV; + colors += 4; + norms += 3; + } +} +*/ + +#undef QWC + +ObjPipeline* +makeDefaultPipeline(void) +{ + if(defaultMatPipe == nil){ + MatPipeline *pipe = new MatPipeline(PLATFORM_PS2); + pipe->attribs[AT_XYZ] = &attribXYZ; + pipe->attribs[AT_UV] = &attribUV; + pipe->attribs[AT_RGBA] = &attribRGBA; + pipe->attribs[AT_NORMAL] = &attribNormal; + uint32 vertCount = MatPipeline::getVertCount(VU_Lights,4,3,2); + pipe->setTriBufferSizes(4, vertCount); + pipe->vifOffset = pipe->inputStride*vertCount; + pipe->uninstanceCB = genericUninstanceCB; + defaultMatPipe = pipe; + } + + if(defaultObjPipe == nil){ + ObjPipeline *opipe = new ObjPipeline(PLATFORM_PS2); + defaultObjPipe = opipe; + } + return defaultObjPipe; +} + +ObjPipeline* +makeSkinPipeline(void) +{ + MatPipeline *pipe = new MatPipeline(PLATFORM_PS2); + pipe->pluginID = ID_SKIN; + pipe->pluginData = 1; + pipe->attribs[AT_XYZ] = &attribXYZ; + pipe->attribs[AT_UV] = &attribUV; + pipe->attribs[AT_RGBA] = &attribRGBA; + pipe->attribs[AT_NORMAL] = &attribNormal; + pipe->attribs[AT_NORMAL+1] = &attribWeights; + uint32 vertCount = MatPipeline::getVertCount(VU_Lights-0x100, 5, 3, 2); + pipe->setTriBufferSizes(5, vertCount); + pipe->vifOffset = pipe->inputStride*vertCount; + pipe->instanceCB = skinInstanceCB; + pipe->uninstanceCB = genericUninstanceCB; + pipe->preUninstCB = skinPreCB; + pipe->postUninstCB = skinPostCB; + + ObjPipeline *opipe = new ObjPipeline(PLATFORM_PS2); + opipe->pluginID = ID_SKIN; + opipe->pluginData = 1; + opipe->groupPipeline = pipe; + return opipe; +} + +ObjPipeline* +makeMatFXPipeline(void) +{ + MatPipeline *pipe = new MatPipeline(PLATFORM_PS2); + pipe->pluginID = ID_MATFX; + pipe->pluginData = 0; + pipe->attribs[AT_XYZ] = &attribXYZ; + pipe->attribs[AT_UV] = &attribUV; + pipe->attribs[AT_RGBA] = &attribRGBA; + pipe->attribs[AT_NORMAL] = &attribNormal; + uint32 vertCount = MatPipeline::getVertCount(0x3C5, 4, 3, 3); + pipe->setTriBufferSizes(4, vertCount); + pipe->vifOffset = pipe->inputStride*vertCount; + pipe->uninstanceCB = genericUninstanceCB; + + ObjPipeline *opipe = new ObjPipeline(PLATFORM_PS2); + opipe->pluginID = ID_MATFX; + opipe->pluginData = 0; + opipe->groupPipeline = pipe; + return opipe; +} + +// Skin + +Stream* +readNativeSkin(Stream *stream, int32, void *object, int32 offset) +{ + uint8 header[4]; + 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_PS2){ + RWERROR((ERR_PLATFORM, platform)); + return nil; + } + stream->read(header, 4); + Skin *skin = new Skin; + *PLUGINOFFSET(Skin*, geometry, offset) = skin; + + // numUsedBones and numWeights appear in/after 34003 but not in/before 33002 + // (probably rw::version >= 0x34000) + bool oldFormat = header[1] == 0; + + // Use numBones for numUsedBones to allocate data + if(oldFormat) + skin->init(header[0], header[0], 0); + else + skin->init(header[0], header[1], 0); + skin->numWeights = header[2]; + + if(!oldFormat) + stream->read(skin->usedBones, skin->numUsedBones); + if(skin->numBones) + stream->read(skin->inverseMatrices, skin->numBones*64); + + // dummy data in case we need to write data in the new format + if(oldFormat){ + skin->numWeights = 4; + for(int32 i = 0; i < skin->numUsedBones; i++) + skin->usedBones[i] = i; + } + + if(!oldFormat) + // last 3 ints are split data as in the other formats + // TODO: what are the other 4? + stream->seek(7*4); + return stream; +} + +Stream* +writeNativeSkin(Stream *stream, int32 len, void *object, int32 offset) +{ + uint8 header[4]; + + writeChunkHeader(stream, ID_STRUCT, len-12); + stream->writeU32(PLATFORM_PS2); + Skin *skin = *PLUGINOFFSET(Skin*, object, offset); + // not sure which version introduced the new format + bool oldFormat = version < 0x34000; + header[0] = skin->numBones; + if(oldFormat){ + header[1] = 0; + header[2] = 0; + }else{ + header[1] = skin->numUsedBones; + header[2] = skin->numWeights; + } + header[3] = 0; + stream->write(header, 4); + + if(!oldFormat) + stream->write(skin->usedBones, skin->numUsedBones); + stream->write(skin->inverseMatrices, skin->numBones*64); + if(!oldFormat){ + uint32 buffer[7] = { 0, 0, 0, 0, 0, 0, 0 }; + stream->write(buffer, 7*4); + } + return stream; +} + +int32 +getSizeNativeSkin(void *object, int32 offset) +{ + Skin *skin = *PLUGINOFFSET(Skin*, object, offset); + if(skin == nil) + return -1; + int32 size = 12 + 4 + 4 + skin->numBones*64; + // not sure which version introduced the new format + if(version >= 0x34000) + size += skin->numUsedBones + 16 + 12; + return size; +} + +void +instanceSkinData(Geometry*, Mesh *m, Skin *skin, uint32 *data) +{ + uint16 j; + float32 *weights = (float32*)data; + uint32 *indices = data; + for(uint32 i = 0; i < m->numIndices; i++){ + j = m->indices[i]; + for(int32 k = 0; k < 4; k++){ + *weights++ = skin->weights[j*4+k]; + *indices &= ~0x3FF; + *indices++ |= skin->indices[j*4+k] && skin->weights[j*4+k] ? + (skin->indices[j*4+k]+1) << 2 : 0; + } + } +} + +void +skinInstanceCB(MatPipeline *, Geometry *g, Mesh *m, uint8 **data) +{ + Skin *skin = *PLUGINOFFSET(Skin*, g, skinGlobals.offset); + if(skin == nil) + return; + instanceSkinData(g, m, skin, (uint32*)data[4]); +} + +// TODO: call base function perhaps? +int32 +findVertexSkin(Geometry *g, uint32 flags[], uint32 mask, Vertex *v) +{ + Skin *skin = *PLUGINOFFSET(Skin*, g, skinGlobals.offset); + float32 *wghts = nil; + uint8 *inds = nil; + if(skin){ + wghts = skin->weights; + inds = skin->indices; + } + + float32 *verts = g->morphTargets[0].vertices; + float32 *tex = g->texCoords[0]; + float32 *tex1 = g->texCoords[1]; + float32 *norms = g->morphTargets[0].normals; + uint8 *cols = g->colors; + + for(int32 i = 0; i < g->numVertices; i++){ + uint32 flag = flags ? flags[i] : ~0; + if(mask & flag & 0x1 && + !(verts[0] == v->p[0] && verts[1] == v->p[1] && verts[2] == v->p[2])) + goto cont; + if(mask & flag & 0x10 && + !(norms[0] == v->n[0] && norms[1] == v->n[1] && norms[2] == v->n[2])) + goto cont; + if(mask & flag & 0x100 && + !(cols[0] == v->c[0] && cols[1] == v->c[1] && cols[2] == v->c[2] && cols[3] == v->c[3])) + goto cont; + if(mask & flag & 0x1000 && + !(tex[0] == v->t[0] && tex[1] == v->t[1])) + goto cont; + if(mask & flag & 0x2000 && + !(tex1[0] == v->t1[0] && tex1[1] == v->t1[1])) + goto cont; + if(mask & flag & 0x10000 && + !(wghts[0] == v->w[0] && wghts[1] == v->w[1] && + wghts[2] == v->w[2] && wghts[3] == v->w[3] && + inds[0] == v->i[0] && inds[1] == v->i[1] && + inds[2] == v->i[2] && inds[3] == v->i[3])) + goto cont; + return i; + cont: + verts += 3; + tex += 2; + norms += 3; + cols += 4; + wghts += 4; + inds += 4; + } + return -1; +} + +void +insertVertexSkin(Geometry *geo, int32 i, uint32 mask, Vertex *v) +{ + Skin *skin = *PLUGINOFFSET(Skin*, geo, skinGlobals.offset); + insertVertex(geo, i, mask, v); + if(mask & 0x10000){ + memcpy(&skin->weights[i*4], v->w, 16); + memcpy(&skin->indices[i*4], v->i, 4); + } +} + +/* +void +skinUninstanceCB(MatPipeline*, Geometry *geo, uint32 flags[], Mesh *mesh, uint8 *data[]) +{ + float32 *verts = (float32*)data[AT_XYZ]; + float32 *texcoords = (float32*)data[AT_UV]; + uint8 *colors = (uint8*)data[AT_RGBA]; + int8 *norms = (int8*)data[AT_NORMAL]; + uint32 *wghts = (uint32*)data[AT_NORMAL+1]; + uint32 mask = 0x1; // vertices + if(geo->geoflags & Geometry::NORMALS) + mask |= 0x10; + if(geo->geoflags & Geometry::PRELIT) + mask |= 0x100; + if(geo->numTexCoordSets > 0) + mask |= 0x1000; + mask |= 0x10000; + + Vertex v; + for(uint32 i = 0; i < mesh->numIndices; i++){ + if(mask & 0x1) + memcpy(&v.p, verts, 12); + if(mask & 0x10){ + v.n[0] = norms[0]/127.0f; + v.n[1] = norms[1]/127.0f; + v.n[2] = norms[2]/127.0f; + } + if(mask & 0x100) + memcpy(&v.c, colors, 4); + if(mask & 0x1000) + memcpy(&v.t, texcoords, 8); + for(int j = 0; j < 4; j++){ + ((uint32*)v.w)[j] = wghts[j] & ~0x3FF; + v.i[j] = (wghts[j] & 0x3FF) >> 2; + if(v.i[j]) v.i[j]--; + if(v.w[j] == 0.0f) v.i[j] = 0; + } + int32 idx = findVertexSkin(geo, flags, mask, &v); + if(idx < 0) + idx = geo->numVertices++; + mesh->indices[i] = idx; + flags[idx] = mask; + insertVertexSkin(geo, idx, mask, &v); + verts += 3; + texcoords += 2; + colors += 4; + norms += 3; + wghts += 4; + } +} +*/ + +void +skinPreCB(MatPipeline*, Geometry *geo) +{ + Skin *skin = *PLUGINOFFSET(Skin*, geo, skinGlobals.offset); + if(skin == nil) + return; + uint8 *data = skin->data; + float *invMats = skin->inverseMatrices; + // meshHeader->totalIndices is highest possible number of vertices again + skin->init(skin->numBones, skin->numBones, geo->meshHeader->totalIndices); + memcpy(skin->inverseMatrices, invMats, skin->numBones*64); + delete[] data; +} + +void +skinPostCB(MatPipeline*, Geometry *geo) +{ + Skin *skin = *PLUGINOFFSET(Skin*, geo, skinGlobals.offset); + if(skin){ + skin->findNumWeights(geo->numVertices); + skin->findUsedBones(geo->numVertices); + } +} + +// ADC + +int32 adcOffset; + +int8* +getADCbits(Geometry *geo) +{ + int8 *bits = nil; + if(adcOffset){ + ADCData *adc = PLUGINOFFSET(ADCData, geo, adcOffset); + if(adc->adcFormatted) + bits = adc->adcBits; + } + return bits; +} + +int8* +getADCbitsForMesh(Geometry *geo, Mesh *mesh) +{ + int8 *bits = getADCbits(geo); + if(bits == nil) + return nil; + int32 n = mesh - geo->meshHeader->mesh; + for(int32 i = 0; i < n; i++) + bits += geo->meshHeader->mesh[i].numIndices; + return bits; +} + +// TODO +void +convertADC(Geometry*) +{ +} + +// Not optimal but works +void +unconvertADC(Geometry *g) +{ + ADCData *adc = PLUGINOFFSET(ADCData, g, adcOffset); + if(!adc->adcFormatted) + return; + int8 *b = adc->adcBits; + MeshHeader *h = new MeshHeader; + h->flags = g->meshHeader->flags; // should be tristrip + h->numMeshes = g->meshHeader->numMeshes; + h->mesh = new Mesh[h->numMeshes]; + Mesh *oldm = g->meshHeader->mesh; + Mesh *newm = h->mesh; + h->totalIndices = 0; + for(int32 i = 0; i < h->numMeshes; i++){ + newm->material = oldm->material; + newm->numIndices = oldm->numIndices; + for(uint32 j = 0; j < oldm->numIndices; j++) + if(*b++) + newm->numIndices += 2; + h->totalIndices += newm->numIndices; + newm++; + oldm++; + } + h->allocateIndices(); + b = adc->adcBits; + oldm = g->meshHeader->mesh; + newm = h->mesh; + for(int32 i = 0; i < h->numMeshes; i++){ + int32 n = 0; + for(uint32 j = 0; j < oldm->numIndices; j++){ + if(*b++){ + newm->indices[n++] = oldm->indices[j-1]; + newm->indices[n++] = oldm->indices[j-1]; + } + newm->indices[n++] = oldm->indices[j]; + } + newm++; + oldm++; + } + delete g->meshHeader; + g->meshHeader = h; + adc->adcFormatted = 0; + delete[] adc->adcBits; + adc->adcBits = nil; + adc->numBits = 0; +} + +void +allocateADC(Geometry *geo) +{ + ADCData *adc = PLUGINOFFSET(ADCData, geo, adcOffset); + adc->adcFormatted = 1; + adc->numBits = geo->meshHeader->totalIndices; + int32 size = adc->numBits+3 & ~3; + adc->adcBits = new int8[size]; + memset(adc->adcBits, 0, size); +} + +static void* +createADC(void *object, int32 offset, int32) +{ + ADCData *adc = PLUGINOFFSET(ADCData, object, offset); + adc->adcFormatted = 0; + return object; +} + +static void* +copyADC(void *dst, void *src, int32 offset, int32) +{ + ADCData *dstadc = PLUGINOFFSET(ADCData, dst, offset); + ADCData *srcadc = PLUGINOFFSET(ADCData, src, offset); + dstadc->adcFormatted = srcadc->adcFormatted; + if(!dstadc->adcFormatted) + return dst; + dstadc->numBits = srcadc->numBits; + int32 size = dstadc->numBits+3 & ~3; + dstadc->adcBits = new int8[size]; + memcpy(dstadc->adcBits, srcadc->adcBits, size); + return dst; +} + +static void* +destroyADC(void *object, int32 offset, int32) +{ + ADCData *adc = PLUGINOFFSET(ADCData, object, offset); + if(adc->adcFormatted) + delete[] adc->adcBits; + return object; +} + +static Stream* +readADC(Stream *stream, int32, void *object, int32 offset, int32) +{ + ADCData *adc = PLUGINOFFSET(ADCData, object, offset); + if(!findChunk(stream, ID_ADC, nil, nil)){ + RWERROR((ERR_CHUNK, "ADC")); + return nil; + } + adc->numBits = stream->readI32(); + adc->adcFormatted = 1; + if(adc->numBits == 0){ + adc->adcBits = nil; + adc->numBits = 0; + return stream; + } + int32 size = adc->numBits+3 & ~3; + adc->adcBits = new int8[size]; + stream->read(adc->adcBits, size); + return stream; +} + +static Stream* +writeADC(Stream *stream, int32 len, void *object, int32 offset, int32) +{ + ADCData *adc = PLUGINOFFSET(ADCData, object, offset); + Geometry *geometry = (Geometry*)object; + writeChunkHeader(stream, ID_ADC, len-12); + if(geometry->geoflags & Geometry::NATIVE){ + stream->writeI32(0); + return stream; + } + stream->writeI32(adc->numBits); + int32 size = adc->numBits+3 & ~3; + stream->write(adc->adcBits, size); + return stream; +} + +static int32 +getSizeADC(void *object, int32 offset, int32) +{ + Geometry *geometry = (Geometry*)object; + ADCData *adc = PLUGINOFFSET(ADCData, object, offset); + if(!adc->adcFormatted) + return 0; + if(geometry->geoflags & Geometry::NATIVE) + return 16; + return 16 + (adc->numBits+3 & ~3); +} + +void +registerADCPlugin(void) +{ + adcOffset = Geometry::registerPlugin(sizeof(ADCData), ID_ADC, + createADC, destroyADC, copyADC); + Geometry::registerPluginStream(ID_ADC, + readADC, + writeADC, + getSizeADC); +} + +// misc stuff + +void +printDMA(InstanceData *inst) +{ + uint32 *tag = (uint32*)inst->data; + for(;;){ + switch(tag[0]&0x70000000){ + case DMAcnt: + printf("%08x %08x\n", tag[0], tag[1]); + tag += (1+(tag[0]&0xFFFF))*4; + break; + + case DMAref: + printf("%08x %08x\n", tag[0], tag[1]); + tag += 4; + break; + + case DMAret: + printf("%08x %08x\n", tag[0], tag[1]); + return; + } + } +} + +void +sizedebug(InstanceData *inst) +{ + if(inst->arePointersFixed == 2) + return; + uint32 *base = (uint32*)inst->data; + uint32 *tag = (uint32*)inst->data; + uint32 *last = nil; + for(;;){ + switch(tag[0]&0x70000000){ + case DMAcnt: + tag += (1+(tag[0]&0xFFFF))*4; + break; + + case DMAref: + last = base + tag[1]*4 + (tag[0]&0xFFFF)*4; + tag += 4; + break; + + case DMAret: + tag += (1+(tag[0]&0xFFFF))*4; + uint32 diff; + if(!last) + diff = (uint8*)tag - (uint8*)base; + else + diff = (uint8*)last - (uint8*)base; + printf("%x %x %x\n", inst->dataSize-diff, diff, inst->dataSize); + return; + + default: + printf("unkown DMAtag: %X %X\n", tag[0], tag[1]); + break; + } + } +} + +} +} diff --git a/src/ps2/ps2raster.cpp b/src/ps2/ps2raster.cpp new file mode 100644 index 0000000..c30f4f7 --- /dev/null +++ b/src/ps2/ps2raster.cpp @@ -0,0 +1,674 @@ +#include +#include +#include +#include + +#include "../rwbase.h" +#include "../rwerror.h" +#include "../rwplg.h" +#include "../rwpipeline.h" +#include "../rwobjects.h" +#include "../rwengine.h" +#include "rwps2.h" + +#define PLUGIN_ID 0 + +#define max(a, b) ((a) > (b) ? (a) : (b)) + +namespace rw { +namespace ps2 { + +int32 nativeRasterOffset; + +#define MAXLEVEL(r) ((r)->tex1[1]>>18 & 0x3F) +#define SETMAXLEVEL(r, l) ((r)->tex1[1] = (r)->tex1[1]&~0xFF0000 | l<<18) +#define SETKL(r, val) ((r)->tex1[1] = (r)->tex1[1]&~0xFFFF | (uint16)(val)) +static bool32 noNewStyleRasters; + +// i don't really understand this, stolen from RW +static void +ps2MinSize(int32 psm, int32 flags, int32 *minw, int32 *minh) +{ + *minh = 1; + switch(psm){ + case 0x00: + case 0x30: + *minw = 2; // 32 bit + break; + case 0x02: + case 0x0A: + case 0x32: + case 0x3A: + *minw = 4; // 16 bit + break; + case 0x01: + case 0x13: + case 0x14: + case 0x1B: + case 0x24: + case 0x2C: + case 0x31: + *minw = 8; // everything else + break; + } + if(flags & 0x2 && psm == 0x13){ // PSMT8 + *minw = 16; + *minh = 4; + } + if(flags & 0x4 && psm == 0x14){ // PSMT4 + *minw = 32; + *minh = 4; + } +} + +struct dword +{ + uint32 lo; + uint32 hi; +}; + +#define ALIGN64(x) ((x) + 0x3F & ~0x3F) + +static void +rasterCreate(Raster *raster) +{ + uint64 bufferWidth[7], bufferBase[7]; + int32 pageWidth, pageHeight; + Ps2Raster *ras = PLUGINOFFSET(Ps2Raster, raster, nativeRasterOffset); + + //printf("%x %x %x %x\n", raster->format, raster->flags, raster->type, noNewStyleRasters); + assert(raster->type == 4); // Texture + switch(raster->depth){ + case 4: + pageWidth = 128; + pageHeight = 128; + break; + case 8: + pageWidth = 128; + pageHeight = 64; + break; + case 16: + pageWidth = 64; + pageHeight = 64; + break; + case 32: + pageWidth = 64; + pageHeight = 32; + break; + default: + assert(0 && "unsupported depth"); + } + int32 logw = 0, logh = 0; + int32 s; + for(s = 1; s < raster->width; s *= 2) + logw++; + for(s = 1; s < raster->height; s *= 2) + logh++; + SETKL(ras, 0xFC0); + //printf("%d %d %d %d\n", raster->width, logw, raster->height, logh); + ras->tex0[0] |= (raster->width < pageWidth ? pageWidth : raster->width)/64 << 14; + ras->tex0[0] |= logw << 26; + ras->tex0[0] |= logh << 30; + ras->tex0[1] |= logh >> 2; + + int32 paletteWidth, paletteHeight, paletteDepth; + int32 palettePagewidth, palettePageheight; + if(raster->format & (Raster::PAL4 | Raster::PAL8)) + switch(raster->format & 0xF00){ + case Raster::C1555: + ras->tex0[1] |= 0xA << 19; // PSMCT16S + paletteDepth = 2; + palettePagewidth = palettePageheight = 64; + break; + case Raster::C8888: + // PSMCT32 + paletteDepth = 4; + palettePagewidth = 64; + palettePageheight = 32; + break; + default: + assert(0 && "unsupported palette format\n"); + } + if(raster->format & Raster::PAL4){ + ras->tex0[0] |= 0x14 << 20; // PSMT4 + ras->tex0[1] |= 1<<29 | 1<<2; // CLD 1, TCC RGBA + paletteWidth = 8; + paletteHeight = 2; + }else if(raster->format & Raster::PAL8){ + ras->tex0[0] |= 0x13 << 20; // PSMT8 + ras->tex0[1] |= 1<<29 | 1<<2; // CLD 1, TCC RGBA + paletteWidth = paletteHeight = 16; + }else{ + paletteWidth = 0; + paletteHeight = 0; + paletteDepth = 0; + palettePagewidth = 0; + palettePageheight = 0; + switch(raster->format & 0xF00){ + case Raster::C1555: + ras->tex0[0] |= 0xA << 20; // PSMCT16S + ras->tex0[1] |= 1 << 2; // TCC RGBA + break; + case Raster::C8888: + // PSMCT32 + ras->tex0[1] |= 1 << 2; // TCC RGBA + break; + case Raster::C888: + ras->tex0[0] |= 1 << 20; // PSMCT24 + break; + default: + assert(0 && "unsupported raster format\n"); + } + } + + for(int i = 0; i < 7; i++){ + bufferWidth[i] = 1; + bufferBase[i] = 0; + } + + int32 mipw, miph; + int32 n; + int32 nPagW, nPagH; + int32 w = raster->width; + int32 h = raster->height; + int32 d = raster->depth; + raster->stride = w*d/8; + + if(raster->format & Raster::MIPMAP){ + static uint32 blockOffset32_24_8[8] = { 0, 2, 2, 8, 8, 10, 10, 32 }; + static uint32 blockOffset16_4[8] = { 0, 1, 4, 5, 16, 17, 20, 21 }; + static uint32 blockOffset16S[8] = { 0, 1, 8, 9, 4, 5, 12, 13 }; + uint64 lastBufferWidth; + mipw = w; + miph = h; + lastBufferWidth = max(pageWidth, w)/64; + ras->texelSize = 0; + int32 gsoffset = 0; + int32 gsaddress = 0; + for(n = 0; n < 7; n++){ + if(w >= 8 && h >= 8 && (mipw < 8 || miph < 8)) + break; + ras->texelSize += ALIGN64(mipw*miph*d/8); + bufferWidth[n] = max(pageWidth, mipw)/64; + + if(bufferWidth[n] != lastBufferWidth){ + nPagW = ((w >> n-1) + pageWidth-1)/pageWidth; + nPagH = ((h >> n-1) + pageHeight-1)/pageHeight; + gsaddress = (gsoffset + nPagW*nPagH*0x800) & ~0x7FF; + } + lastBufferWidth = bufferWidth[n]; + gsaddress = ALIGN64(gsaddress); + uint32 b = gsaddress/256 & 7; + switch(ras->tex0[0]>>20 & 0x3F){ + case 0: case 1: case 0x13: + b = blockOffset32_24_8[b]; + break; + case 2: case 0x14: + b = blockOffset16_4[b]; + break; + case 0xA: + b = blockOffset16S[b]; + break; + default: + // can't happen + break; + } + bufferBase[n] = b + (gsaddress>>11 << 5); + int32 stride = bufferWidth[n]/64*d/8; + gsaddress = ALIGN64(miph*stride/4 + gsoffset); + + mipw /= 2; + miph /= 2; + } + assert(0); + }else{ + ras->texelSize = raster->stride*raster->height+0xF & ~0xF; + ras->paletteSize = paletteWidth*paletteHeight*paletteDepth; + ras->miptbp1[0] |= 1<<14; // TBW1 + ras->miptbp1[1] |= 1<<2 | 1<<22; // TBW2,3 + ras->miptbp2[0] |= 1<<14; // TBW4 + ras->miptbp2[1] |= 1<<2 | 1<<22; // TBW5,6 + SETMAXLEVEL(ras, 0); + nPagW = (raster->width + pageWidth-1)/pageWidth; + nPagH = (raster->height + pageHeight-1)/pageHeight; + bufferBase[0] = 0; + bufferWidth[0] = nPagW * (pageWidth >> 6); + ras->gsSize = (nPagW*nPagH*0x800)&~0x7FF; + if(ras->paletteSize){ + // BITBLTBUF DBP + if(pageWidth*nPagW > raster->width || + pageHeight*nPagH > raster->height) + ras->tex1[0] = (ras->gsSize >> 6) - 4; + else + ras->tex1[0] = ras->gsSize >> 6; + nPagW = (paletteWidth + palettePagewidth-1)/palettePagewidth; + nPagH = (paletteHeight + palettePageheight-1)/palettePageheight; + ras->gsSize += (nPagW*nPagH*0x800)&~0x7FF; + }else + ras->tex1[0] = 0; + } + + // allocate data and fill with GIF packets + ras->texelSize = ras->texelSize+0xF & ~0xF; + int32 numLevels = MAXLEVEL(ras)+1; + if(noNewStyleRasters || + (raster->width*raster->height*raster->depth & ~0x7F) >= 0x3FFF80){ + assert(0); + }else{ + ras->flags |= 1; // include GIF packets + int32 psm = ras->tex0[0]>>20 & 0x3F; + //int32 cpsm = ras->tex0[1]>>19 & 0x3F; + if(psm == 0x13){ // PSMT8 + ras->flags |= 2; + // TODO: stuff + } + if(psm == 0x14){ // PSMT4 + // swizzle flag probably depends on version :/ + if(rw::version > 0x31000) + ras->flags |= 4; + // TODO: stuff + } + ras->texelSize = 0x50*numLevels; // GIF packets + int32 minW, minH; + ps2MinSize(psm, ras->flags, &minW, &minH); + w = raster->width; + h = raster->height; + n = numLevels; + while(n--){ + mipw = w < minW ? minW : w; + miph = h < minH ? minH : h; + ras->texelSize += mipw*miph*raster->depth/8+0xF & ~0xF; + w /= 2; + h /= 2; + } + if(ras->paletteSize){ + if(rw::version > 0x31000 && paletteHeight == 2) + paletteHeight = 3; + ras->paletteSize = 0x50 + + paletteDepth*paletteWidth*paletteHeight; + } + // TODO: allocate space for more DMA packets + ras->dataSize = ras->paletteSize+ras->texelSize; + uint8 *data = new uint8[ras->dataSize]; + assert(data); + ras->data = data; + raster->texels = data + 0x50; + if(ras->paletteSize) + raster->palette = data + ras->texelSize + 0x50; + uint32 *p = (uint32*)data; + w = raster->width; + h = raster->height; + for(n = 0; n < numLevels; n++){ + mipw = w < minW ? minW : w; + miph = h < minH ? minH : h; + + // GIF tag + *p++ = 3; // NLOOP = 3 + *p++ = 0x10000000; // NREG = 1 + *p++ = 0xE; // A+D + *p++ = 0; + + // TRXPOS + *p++ = 0; // TODO + *p++ = 0; // TODO + *p++ = 0x51; + *p++ = 0; + + // TRXREG + if(ras->flags & 2 && psm == 0x13 || + ras->flags & 4 && psm == 0x14){ + *p++ = mipw/2; + *p++ = miph/2; + }else{ + *p++ = mipw; + *p++ = miph; + } + *p++ = 0x52; + *p++ = 0; + + // TRXDIR + *p++ = 0; // host -> local + *p++ = 0; + *p++ = 0x53; + *p++ = 0; + + // GIF tag + uint32 sz = mipw*miph*raster->depth/8 + 0xF >> 4; + *p++ = sz; + *p++ = 0x08000000; // IMAGE + *p++ = 0; + *p++ = 0; + + p += sz*4; + w /= 2; + h /= 2; + } + + if(ras->paletteSize){ + p = (uint32*)(raster->palette - 0x50); + // GIF tag + *p++ = 3; // NLOOP = 3 + *p++ = 0x10000000; // NREG = 1 + *p++ = 0xE; // A+D + *p++ = 0; + + // TRXPOS + *p++ = 0; // TODO + *p++ = 0; // TODO + *p++ = 0x51; + *p++ = 0; + + // TRXREG + *p++ = paletteWidth; + *p++ = paletteHeight; + *p++ = 0x52; + *p++ = 0; + + // TRXDIR + *p++ = 0; // host -> local + *p++ = 0; + *p++ = 0x53; + *p++ = 0; + + // GIF tag + uint32 sz = ras->paletteSize - 0x50 + 0xF >> 4; + *p++ = sz; + *p++ = 0x08000000; // IMAGE + *p++ = 0; + *p++ = 0; + } + } +} + +static uint8* +rasterLock(Raster *raster, int32 level) +{ + // TODO + (void)raster; + (void)level; + return nil; +} + +static void +rasterUnlock(Raster *raster, int32 level) +{ + // TODO + (void)raster; + (void)level; +} + +static int32 +rasterNumLevels(Raster *raster) +{ + Ps2Raster *ras = PLUGINOFFSET(Ps2Raster, raster, nativeRasterOffset); + if(raster->texels == nil) return 0; + if(raster->format & Raster::MIPMAP) + return MAXLEVEL(ras)+1; + return 1; +} + +static void* +createNativeRaster(void *object, int32 offset, int32) +{ + Ps2Raster *raster = PLUGINOFFSET(Ps2Raster, object, offset); + raster->tex0[0] = 0; + raster->tex0[1] = 0; + raster->tex1[0] = 0; + raster->tex1[1] = 0; + raster->miptbp1[0] = 0; + raster->miptbp1[1] = 0; + raster->miptbp2[0] = 0; + raster->miptbp2[1] = 0; + raster->texelSize = 0; + raster->paletteSize = 0; + raster->gsSize = 0; + raster->flags = 0; + SETKL(raster, 0xFC0); + + raster->dataSize = 0; + raster->data = nil; + return object; +} + +static void* +destroyNativeRaster(void *object, int32 offset, int32) +{ + // TODO + (void)offset; + return object; +} + +static void* +copyNativeRaster(void *dst, void *src, int32 offset, int32) +{ + Ps2Raster *dstraster = PLUGINOFFSET(Ps2Raster, dst, offset); + Ps2Raster *srcraster = PLUGINOFFSET(Ps2Raster, src, offset); + *dstraster = *srcraster; + return dst; +} + +static Stream* +readMipmap(Stream *stream, int32, void *object, int32 offset, int32) +{ + uint16 val = stream->readI32(); + Texture *tex = (Texture*)object; + if(tex->raster == nil) + return stream; + Ps2Raster *raster = PLUGINOFFSET(Ps2Raster, tex->raster, offset); + SETKL(raster, val); + return stream; +} + +static Stream* +writeMipmap(Stream *stream, int32, void *object, int32 offset, int32) +{ + Texture *tex = (Texture*)object; + if(tex->raster) + return nil; + Ps2Raster *raster = PLUGINOFFSET(Ps2Raster, tex->raster, offset); + stream->writeI32(raster->tex1[1]&0xFFFF); + return stream; +} + +static int32 +getSizeMipmap(void*, int32, int32) +{ + return rw::platform == PLATFORM_PS2 ? 4 : 0; +} + +void +registerNativeRaster(void) +{ + nativeRasterOffset = Raster::registerPlugin(sizeof(Ps2Raster), + 0x12340000 | PLATFORM_PS2, + createNativeRaster, + destroyNativeRaster, + copyNativeRaster); + driver[PLATFORM_PS2].rasterNativeOffset = nativeRasterOffset; + driver[PLATFORM_PS2].rasterCreate = rasterCreate; + driver[PLATFORM_PS2].rasterLock = rasterLock; + driver[PLATFORM_PS2].rasterUnlock = rasterUnlock; + driver[PLATFORM_PS2].rasterNumLevels = rasterNumLevels; + + Texture::registerPlugin(0, ID_SKYMIPMAP, nil, nil, nil); + Texture::registerPluginStream(ID_SKYMIPMAP, readMipmap, writeMipmap, getSizeMipmap); +} + +struct StreamRasterExt +{ + int32 width; + int32 height; + int32 depth; + uint16 rasterFormat; + int16 type; + uint32 tex0[2]; + uint32 tex1[2]; + uint32 miptbp1[2]; + uint32 miptbp2[2]; + uint32 texelSize; + uint32 paletteSize; + uint32 gsSize; + uint32 mipmapVal; +}; + +Texture* +readNativeTexture(Stream *stream) +{ + uint32 length, oldversion, version; + uint32 fourcc; + Raster *raster; + Ps2Raster *natras; + if(!findChunk(stream, ID_STRUCT, nil, nil)){ + RWERROR((ERR_CHUNK, "STRUCT")); + return nil; + } + fourcc = stream->readU32(); + if(fourcc != FOURCC_PS2){ + RWERROR((ERR_PLATFORM, fourcc)); + return nil; + } + Texture *tex = Texture::create(nil); + if(tex == nil) + return nil; + + // Texture + tex->filterAddressing = stream->readU32(); + if(!findChunk(stream, ID_STRING, &length, nil)){ + RWERROR((ERR_CHUNK, "STRING")); + goto fail; + } + stream->read(tex->name, length); + if(!findChunk(stream, ID_STRING, &length, nil)){ + RWERROR((ERR_CHUNK, "STRING")); + goto fail; + } + stream->read(tex->mask, length); + + // Raster + StreamRasterExt streamExt; + oldversion = rw::version; + if(!findChunk(stream, ID_STRUCT, nil, nil)){ + RWERROR((ERR_CHUNK, "STRUCT")); + goto fail; + } + if(!findChunk(stream, ID_STRUCT, nil, &version)){ + RWERROR((ERR_CHUNK, "STRUCT")); + goto fail; + } + stream->read(&streamExt, 0x40); + noNewStyleRasters = streamExt.type < 2; + rw::version = version; + raster = Raster::create(streamExt.width, streamExt.height, + streamExt.depth, streamExt.rasterFormat, + PLATFORM_PS2); + noNewStyleRasters = 0; + rw::version = oldversion; + tex->raster = raster; + natras = PLUGINOFFSET(Ps2Raster, raster, nativeRasterOffset); + //printf("%08X%08X %08X%08X %08X%08X %08X%08X\n", + // ras->tex0[1], ras->tex0[0], ras->tex1[1], ras->tex1[0], + // ras->miptbp1[0], ras->miptbp1[1], ras->miptbp2[0], ras->miptbp2[1]); + natras->tex0[0] = streamExt.tex0[0]; + natras->tex0[1] = streamExt.tex0[1]; + natras->tex1[0] = streamExt.tex1[0]; + natras->tex1[1] = natras->tex1[1]&~0xFF0000 | + streamExt.tex1[1]<<16 & 0xFF0000; + natras->miptbp1[0] = streamExt.miptbp1[0]; + natras->miptbp1[1] = streamExt.miptbp1[1]; + natras->miptbp2[0] = streamExt.miptbp2[0]; + natras->miptbp2[1] = streamExt.miptbp2[1]; + natras->texelSize = streamExt.texelSize; + natras->paletteSize = streamExt.paletteSize; + natras->gsSize = streamExt.gsSize; + SETKL(natras, streamExt.mipmapVal); + //printf("%08X%08X %08X%08X %08X%08X %08X%08X\n", + // ras->tex0[1], ras->tex0[0], ras->tex1[1], ras->tex1[0], + // ras->miptbp1[0], ras->miptbp1[1], ras->miptbp2[0], ras->miptbp2[1]); + + if(!findChunk(stream, ID_STRING, &length, nil)){ + RWERROR((ERR_CHUNK, "STRING")); + goto fail; + } + if(streamExt.type < 2){ + stream->read(raster->texels, length); + }else{ + stream->read(raster->texels-0x50, natras->texelSize); + stream->read(raster->palette-0x50, natras->paletteSize); + } + if(tex->streamReadPlugins(stream)) + return tex; + +fail: + tex->destroy(); + return nil; +} + +void +writeNativeTexture(Texture *tex, Stream *stream) +{ + Raster *raster = tex->raster; + Ps2Raster *ras = PLUGINOFFSET(Ps2Raster, raster, nativeRasterOffset); + int32 chunksize = getSizeNativeTexture(tex); + writeChunkHeader(stream, ID_TEXTURENATIVE, chunksize); + writeChunkHeader(stream, ID_STRUCT, 8); + stream->writeU32(FOURCC_PS2); + stream->writeU32(tex->filterAddressing); + int32 len = strlen(tex->name)+4 & ~3; + writeChunkHeader(stream, ID_STRING, len); + stream->write(tex->name, len); + len = strlen(tex->mask)+4 & ~3; + writeChunkHeader(stream, ID_STRING, len); + stream->write(tex->mask, len); + + int32 sz = ras->texelSize + ras->paletteSize; + writeChunkHeader(stream, ID_STRUCT, 12 + 64 + 12 + sz); + writeChunkHeader(stream, ID_STRUCT, 64); + StreamRasterExt streamExt; + streamExt.width = raster->width; + streamExt.height = raster->height; + streamExt.depth = raster->depth; + streamExt.rasterFormat = raster->format | raster->type; + streamExt.type = 0; + if(ras->flags == 2 && raster->depth == 8) + streamExt.type = 1; + if(ras->flags & 1) + streamExt.type = 2; + streamExt.tex0[0] = ras->tex0[0]; + streamExt.tex0[1] = ras->tex0[1]; + streamExt.tex1[0] = ras->tex1[0]; + streamExt.tex1[1] = ras->tex1[1]>>16 & 0xFF; + streamExt.miptbp1[0] = ras->miptbp1[0]; + streamExt.miptbp1[1] = ras->miptbp1[1]; + streamExt.miptbp2[0] = ras->miptbp2[0]; + streamExt.miptbp2[1] = ras->miptbp2[1]; + streamExt.texelSize = ras->texelSize; + streamExt.paletteSize = ras->paletteSize; + streamExt.gsSize = ras->gsSize; + streamExt.mipmapVal = ras->tex1[1]&0xFFFF; + stream->write(&streamExt, 64); + + writeChunkHeader(stream, ID_STRUCT, sz); + if(streamExt.type < 2){ + stream->write(raster->texels, sz); + }else{ + stream->write(raster->texels-0x50, ras->texelSize); + stream->write(raster->palette-0x50, ras->paletteSize); + } + tex->streamWritePlugins(stream); +} + +uint32 +getSizeNativeTexture(Texture *tex) +{ + uint32 size = 12 + 8; + size += 12 + strlen(tex->name)+4 & ~3; + size += 12 + strlen(tex->mask)+4 & ~3; + size += 12 + 12 + 64 + 12; + Ps2Raster *ras = PLUGINOFFSET(Ps2Raster, tex->raster, nativeRasterOffset); + size += ras->texelSize + ras->paletteSize; + size += 12 + tex->streamGetPluginSize(); + return size; +} + +} +} diff --git a/src/ps2/rwps2.h b/src/ps2/rwps2.h new file mode 100644 index 0000000..27bff0a --- /dev/null +++ b/src/ps2/rwps2.h @@ -0,0 +1,204 @@ +namespace rw { +namespace ps2 { + +void initializePlatform(void); + +struct InstanceData +{ + // 0 - addresses in ref tags need fixing + // 1 - no ref tags, so no fixing + // set by the program: + // 2 - ref tags are fixed, need to unfix before stream write + uint32 arePointersFixed; + uint32 dataSize; + uint8 *data; + Material *material; +}; + +struct InstanceDataHeader : rw::InstanceDataHeader +{ + uint32 numMeshes; + InstanceData *instanceMeshes; +}; + +enum { + VU_Lights = 0x3d0 +}; + +enum PS2Attribs { + AT_V2_32 = 0x64000000, + AT_V2_16 = 0x65000000, + AT_V2_8 = 0x66000000, + AT_V3_32 = 0x68000000, + AT_V3_16 = 0x69000000, + AT_V3_8 = 0x6A000000, + AT_V4_32 = 0x6C000000, + AT_V4_16 = 0x6D000000, + AT_V4_8 = 0x6E000000, + AT_UNSGN = 0x00004000, + + AT_RW = 0x6 +}; + +// Not really types as in RW but offsets +enum PS2AttibTypes { + AT_XYZ = 0, + AT_UV = 1, + AT_RGBA = 2, + AT_NORMAL = 3 +}; + +void *destroyNativeData(void *object, int32, int32); +Stream *readNativeData(Stream *stream, int32 len, void *object, int32, int32); +Stream *writeNativeData(Stream *stream, int32 len, void *object, int32, int32); +int32 getSizeNativeData(void *object, int32, int32); +void registerNativeDataPlugin(void); + +void printDMA(InstanceData *inst); +void sizedebug(InstanceData *inst); + +// only RW_PS2 +void fixDmaOffsets(InstanceData *inst); +void unfixDmaOffsets(InstanceData *inst); +// + +struct PipeAttribute +{ + const char *name; + uint32 attrib; +}; + +extern PipeAttribute attribXYZ; +extern PipeAttribute attribXYZW; +extern PipeAttribute attribUV; +extern PipeAttribute attribUV2; +extern PipeAttribute attribRGBA; +extern PipeAttribute attribNormal; +extern PipeAttribute attribWeights; + +class MatPipeline : public rw::Pipeline +{ +public: + uint32 vifOffset; + uint32 inputStride; + uint32 triStripCount, triListCount; + PipeAttribute *attribs[10]; + void (*instanceCB)(MatPipeline*, Geometry*, Mesh*, uint8**); + void (*uninstanceCB)(MatPipeline*, Geometry*, uint32*, Mesh*, uint8**); + void (*preUninstCB)(MatPipeline*, Geometry*); + void (*postUninstCB)(MatPipeline*, Geometry*); + // RW has more: + // instanceTestCB() + // resEntryAllocCB() + // bridgeCB() + // postMeshCB() + // vu1code + // primtype + + static uint32 getVertCount(uint32 top, uint32 inAttribs, + uint32 outAttribs, uint32 outBufs) { + return (top-outBufs)/(inAttribs*2+outAttribs*outBufs); + } + + MatPipeline(uint32 platform); + void dump(void); + void setTriBufferSizes(uint32 inputStride, uint32 stripCount); + void instance(Geometry *g, InstanceData *inst, Mesh *m); + uint8 *collectData(Geometry *g, InstanceData *inst, Mesh *m, uint8 *data[]); +}; + +class ObjPipeline : public rw::ObjPipeline +{ +public: + MatPipeline *groupPipeline; + // RW has more: + // setupCB() + // finalizeCB() + // lightOffset + // lightSize + + ObjPipeline(uint32 platform); +}; + +struct Vertex { + float32 p[3]; + float32 t[2]; + float32 t1[2]; + uint8 c[4]; + float32 n[3]; + // skin + float32 w[4]; + uint8 i[4]; +}; + +void insertVertex(Geometry *geo, int32 i, uint32 mask, Vertex *v); + +extern ObjPipeline *defaultObjPipe; +extern MatPipeline *defaultMatPipe; + +void genericUninstanceCB(MatPipeline *pipe, Geometry *geo, uint32 flags[], Mesh *mesh, uint8 *data[]); +void genericPreCB(MatPipeline *pipe, Geometry *geo); // skin and ADC +//void defaultUninstanceCB(MatPipeline *pipe, Geometry *geo, uint32 flags[], Mesh *mesh, uint8 *data[]); +void skinInstanceCB(MatPipeline *, Geometry *g, Mesh *m, uint8 **data); +//void skinUninstanceCB(MatPipeline*, Geometry *geo, uint32 flags[], Mesh *mesh, uint8 *data[]); + +ObjPipeline *makeDefaultPipeline(void); +void dumpPipeline(rw::Pipeline *pipe); + +// ADC plugin + +// Each element in adcBits corresponds to an index in Mesh->indices, +// this assumes the Mesh indices are ADC formatted. +// ADCData->numBits != Mesh->numIndices. ADCData->numBits is probably +// equal to Mesh->numIndices before the Mesh gets ADC formatted. +// +// Can't convert between ADC-formatted and non-ADC-formatted yet :( + +struct ADCData +{ + bool32 adcFormatted; + int8 *adcBits; + int32 numBits; +}; +extern int32 adcOffset; +void registerADCPlugin(void); + +int8 *getADCbits(Geometry *geo); +int8 *getADCbitsForMesh(Geometry *geo, Mesh *mesh); +void convertADC(Geometry *g); +void unconvertADC(Geometry *geo); +void allocateADC(Geometry *geo); + +// PDS plugin + +Pipeline *getPDSPipe(uint32 data); +void registerPDSPipe(Pipeline *pipe); +void registerPDSPlugin(int32 n); +void registerPluginPDSPipes(void); + +// Native Texture and Raster + +struct Ps2Raster +{ + uint32 tex0[2]; + uint32 tex1[2]; + uint32 miptbp1[2]; + uint32 miptbp2[2]; + uint32 texelSize; + uint32 paletteSize; + uint32 gsSize; + int8 flags; + + uint8 *data; //tmp + uint32 dataSize; +}; + +extern int32 nativeRasterOffset; +void registerNativeRaster(void); + +Texture *readNativeTexture(Stream *stream); +void writeNativeTexture(Texture *tex, Stream *stream); +uint32 getSizeNativeTexture(Texture *tex); + +} +} diff --git a/src/ps2/rwps2plg.h b/src/ps2/rwps2plg.h new file mode 100644 index 0000000..ac2d3be --- /dev/null +++ b/src/ps2/rwps2plg.h @@ -0,0 +1,22 @@ +namespace rw { +namespace ps2 { + +ObjPipeline *makeSkinPipeline(void); +ObjPipeline *makeMatFXPipeline(void); + +// Skin plugin + +void insertVertexSkin(Geometry *geo, int32 i, uint32 mask, Vertex *v); +int32 findVertexSkin(Geometry *g, uint32 flags[], uint32 mask, Vertex *v); + +Stream *readNativeSkin(Stream *stream, int32, void *object, int32 offset); +Stream *writeNativeSkin(Stream *stream, int32 len, void *object, int32 offset); +int32 getSizeNativeSkin(void *object, int32 offset); + +void instanceSkinData(Geometry *g, Mesh *m, Skin *skin, uint32 *data); + +void skinPreCB(MatPipeline*, Geometry*); +void skinPostCB(MatPipeline*, Geometry*); + +} +} diff --git a/src/rwbase.cpp b/src/rwbase.cpp index 023fbc0..142c76b 100644 --- a/src/rwbase.cpp +++ b/src/rwbase.cpp @@ -26,8 +26,10 @@ int32 version = 0x36003; int32 build = 0xFFFF; #ifdef RW_PS2 int32 platform = PLATFORM_PS2; -#elif RW_OPENGL +#elif RW_WDGL int32 platform = PLATFORM_WDGL; +#elif RW_GL3 + int32 platform = PLATFORM_GL3; #elif RW_D3D9 int32 platform = PLATFORM_D3D9; #else diff --git a/src/rwbase.h b/src/rwbase.h index 31c029d..0b91be8 100644 --- a/src/rwbase.h +++ b/src/rwbase.h @@ -3,6 +3,14 @@ #endif #include +#ifdef RW_GL3 +#define RW_OPENGL +#endif + +#ifdef RW_WDGL +#define RW_OPENGL +#endif + namespace rw { #ifdef RW_PS2