#include #include #include #include #include "../rwbase.h" #include "../rwerror.h" #include "../rwplg.h" #include "../rwpipeline.h" #include "../rwobjects.h" #include "../rwanim.h" #include "../rwengine.h" #include "../rwplugins.h" #include "rwwdgl.h" #ifdef RW_OPENGL #include #endif #define PLUGIN_ID 2 namespace rw { namespace wdgl { static void* driverOpen(void *o, int32, int32) { engine->driver[PLATFORM_WDGL]->defaultPipeline = makeDefaultPipeline(); return o; } static void* driverClose(void *o, int32, int32) { return o; } void registerPlatformPlugins(void) { Driver::registerPlugin(PLATFORM_WDGL, 0, PLATFORM_WDGL, driverOpen, driverClose); } // 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->getMeshes()[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 rwFree(header->attribs); rwFree(header->data); rwFree(header); return object; } Stream* readNativeData(Stream *stream, int32, void *object, int32, int32) { Geometry *geometry = (Geometry*)object; InstanceDataHeader *header = rwNewT(InstanceDataHeader, 1, MEMDUR_EVENT | ID_GEOMETRY); geometry->instData = header; header->platform = PLATFORM_WDGL; header->vbo = 0; header->ibo = 0; header->numAttribs = stream->readU32(); header->attribs = rwNewT(AttribDesc, header->numAttribs, MEMDUR_EVENT | ID_GEOMETRY); stream->read32(header->attribs, header->numAttribs*sizeof(AttribDesc)); header->dataSize = header->attribs[0].stride*geometry->numVertices; header->data = rwNewT(uint8, header->dataSize, MEMDUR_EVENT | ID_GEOMETRY); ASSERTLITTLE; stream->read8(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->write32(header->attribs, header->numAttribs*sizeof(AttribDesc)); ASSERTLITTLE; stream->write8(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; // TODO: allow for REINSTANCE (or not, wdgl can't render) if(geo->instData) return; InstanceDataHeader *header = rwNewT(InstanceDataHeader, 1, MEMDUR_EVENT | ID_GEOMETRY); geo->instData = header; header->platform = PLATFORM_WDGL; header->vbo = 0; header->ibo = 0; header->numAttribs = pipe->numCustomAttribs + 1 + (geo->numTexCoordSets > 0); if(geo->flags & Geometry::PRELIT) header->numAttribs++; if(geo->flags & Geometry::NORMALS) header->numAttribs++; int32 offset = 0; header->attribs = rwNewT(AttribDesc, header->numAttribs, MEMDUR_EVENT | ID_GEOMETRY); 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->flags & Geometry::NORMALS){ a->index = 2; a->type = 1; a->normalized = 1; a->size = 3; a->offset = offset; offset += 4; a++; firstCustom++; } if(geo->flags & 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 = rwNewT(uint8, header->dataSize, MEMDUR_EVENT | ID_GEOMETRY); } a = header->attribs; for(int32 i = 0; i < header->numAttribs; i++) a[i].stride = offset; uint8 *p = header->data + a->offset; V3d *vert = geo->morphTargets->vertices; for(int32 i = 0; i < geo->numVertices; i++){ packattrib(p, (float32*)vert, a); vert++; p += a->stride; } a++; if(geo->numTexCoordSets){ p = header->data + a->offset; TexCoords *texcoord = geo->texCoords[0]; for(int32 i = 0; i < geo->numVertices; i++){ packattrib(p, (float32*)texcoord, a, 512.0f); texcoord++; p += a->stride; } a++; } if(geo->flags & Geometry::NORMALS){ p = header->data + a->offset; V3d *norm = geo->morphTargets->normals; for(int32 i = 0; i < geo->numVertices; i++){ packattrib(p, (float32*)norm, a); norm++; p += a->stride; } a++; } if(geo->flags & Geometry::PRELIT){ // TODO: this seems too complicated p = header->data + a->offset; RGBA *color = geo->colors; float32 f[4]; for(int32 i = 0; i < geo->numVertices; i++){ f[0] = color->red/255.0f; f[1] = color->green/255.0f; f[2] = color->blue/255.0f; f[3] = color->alpha/255.0f; packattrib(p, (float32*)f, a); color++; p += a->stride; } a++; } } static void uninstance(rw::ObjPipeline *rwpipe, Atomic *atomic) { ObjPipeline *pipe = (ObjPipeline*)rwpipe; Geometry *geo = atomic->geometry; if((geo->flags & Geometry::NATIVE) == 0) return; assert(geo->instData != nil); assert(geo->instData->platform == PLATFORM_WDGL); geo->numTriangles = geo->meshHeader->guessNumTriangles(); geo->allocateData(); uint8 *p; TexCoords *texcoord = geo->texCoords[0]; RGBA *color = geo->colors; V3d *vert = geo->morphTargets->vertices; V3d *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((float32*)vert, p, a); vert++; p += a->stride; } break; case 1: // texCoords for(int32 i = 0; i < geo->numVertices; i++){ unpackattrib((float32*)texcoord, p, a, 512.0f); texcoord++; p += a->stride; } break; case 2: // normals for(int32 i = 0; i < geo->numVertices; i++){ unpackattrib((float32*)norm, p, a); norm++; 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->red = f[0]*255.0f; color->green = f[1]*255.0f; color->blue = f[2]*255.0f; color->alpha = f[3]*255.0f; color++; p += a->stride; } break; } } if(pipe->uninstanceCB) pipe->uninstanceCB(geo); geo->generateTriangles(); geo->flags &= ~Geometry::NATIVE; destroyNativeData(geo, 0, 0); } void ObjPipeline::init(void) { this->rw::ObjPipeline::init(PLATFORM_GL3); this->numCustomAttribs = 0; this->impl.instance = wdgl::instance; this->impl.uninstance = wdgl::uninstance; this->instanceCB = nil; this->uninstanceCB = nil; } ObjPipeline* ObjPipeline::create(void) { ObjPipeline *pipe = rwNewT(ObjPipeline, 1, MEMDUR_GLOBAL); pipe->init(); return pipe; } ObjPipeline* makeDefaultPipeline(void) { ObjPipeline *pipe = ObjPipeline::create(); 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 = rwNewT(Skin, 1, MEMDUR_EVENT | ID_SKIN); *PLUGINOFFSET(Skin*, geometry, offset) = skin; int32 numBones = stream->readI32(); skin->init(numBones, 0, 0); stream->read32(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_GL); Skin *skin = *PLUGINOFFSET(Skin*, object, offset); stream->writeI32(skin->numBones); stream->write32(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 = rwNewT(uint8, header->dataSize, MEMDUR_EVENT | ID_GEOMETRY); Skin *skin = Skin::get(g); 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 = Skin::get(geo); 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); rwFree(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); float sum = weights[0] + weights[1] + weights[2] + weights[3]; if(sum){ weights[0] /= sum; weights[1] /= sum; weights[2] /= sum; weights[3] /= sum; } 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); } // Skin static void* skinOpen(void *o, int32, int32) { skinGlobals.pipelines[PLATFORM_WDGL] = makeSkinPipeline(); return o; } static void* skinClose(void *o, int32, int32) { ((ObjPipeline*)skinGlobals.pipelines[PLATFORM_WDGL])->destroy(); skinGlobals.pipelines[PLATFORM_WDGL] = nil; return o; } void initSkin(void) { Driver::registerPlugin(PLATFORM_WDGL, 0, ID_SKIN, skinOpen, skinClose); } ObjPipeline* makeSkinPipeline(void) { ObjPipeline *pipe = ObjPipeline::create(); pipe->pluginID = ID_SKIN; pipe->pluginData = 1; pipe->numCustomAttribs = 2; pipe->instanceCB = skinInstanceCB; pipe->uninstanceCB = skinUninstanceCB; return pipe; } // MatFX static void* matfxOpen(void *o, int32, int32) { matFXGlobals.pipelines[PLATFORM_WDGL] = makeMatFXPipeline(); return o; } static void* matfxClose(void *o, int32, int32) { ((ObjPipeline*)matFXGlobals.pipelines[PLATFORM_WDGL])->destroy(); matFXGlobals.pipelines[PLATFORM_WDGL] = nil; return o; } void initMatFX(void) { Driver::registerPlugin(PLATFORM_WDGL, 0, ID_MATFX, matfxOpen, matfxClose); } ObjPipeline* makeMatFXPipeline(void) { ObjPipeline *pipe = ObjPipeline::create(); 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), ID_RASTERWDGL, 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->pixels); 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 } }