mirror of
https://github.com/aap/librw.git
synced 2025-02-09 01:15:44 +00:00
876 lines
19 KiB
C++
876 lines
19 KiB
C++
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
#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 <GL/glew.h>
|
|
#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
|
|
|
|
}
|
|
}
|