From eb71ca25d0c4f10b39c653d9b9847e38aac198d7 Mon Sep 17 00:00:00 2001
From: aap <aap@papnet.eu>
Date: Mon, 27 Apr 2020 20:51:35 +0200
Subject: [PATCH] started working on gl3 rasters

---
 src/gl/gl3device.cpp         |   4 +-
 src/gl/gl3raster.cpp         | 282 ++++++++++++++++++++++++++++-------
 src/gl/rwgl3.h               |   9 ++
 src/gl/shaders/header.vert   |   4 +-
 src/gl/shaders/header_vs.inc |   4 +-
 src/raster.cpp               |  37 +++++
 src/rwobjects.h              |   5 +
 src/texture.cpp              |   7 +
 8 files changed, 294 insertions(+), 58 deletions(-)

diff --git a/src/gl/gl3device.cpp b/src/gl/gl3device.cpp
index be03612..a3a70f5 100644
--- a/src/gl/gl3device.cpp
+++ b/src/gl/gl3device.cpp
@@ -240,11 +240,13 @@ setActiveTexture(int32 n)
 	}
 }
 
-static void
+uint32
 bindTexture(uint32 texid)
 {
+	uint32 prev = boundTexture[activeTexture];
 	boundTexture[activeTexture] = texid;
 	glBindTexture(GL_TEXTURE_2D, texid);
+	return prev;
 }
 
 // TODO: support mipmaps
diff --git a/src/gl/gl3raster.cpp b/src/gl/gl3raster.cpp
index 43b4580..911b48f 100644
--- a/src/gl/gl3raster.cpp
+++ b/src/gl/gl3raster.cpp
@@ -29,29 +29,36 @@ rasterCreateTexture(Raster *raster)
 	Gl3Raster *natras = PLUGINOFFSET(Gl3Raster, raster, nativeRasterOffset);
 	switch(raster->format & 0xF00){
 	case Raster::C8888:
-		natras->internalFormat = GL_RGBA;
+		natras->internalFormat = GL_RGBA8;
 		natras->format = GL_RGBA;
 		natras->type = GL_UNSIGNED_BYTE;
 		natras->hasAlpha = 1;
+		natras->bbp = 4;
+		raster->depth = 32;
 		break;
 	case Raster::C888:
-		natras->internalFormat = GL_RGB;
+		natras->internalFormat = GL_RGB8;
 		natras->format = GL_RGB;
 		natras->type = GL_UNSIGNED_BYTE;
 		natras->hasAlpha = 0;
+		natras->bbp = 3;
+		raster->depth = 24;
 		break;
 	case Raster::C1555:
-		// TODO: check if this is correct
-		natras->internalFormat = GL_RGBA;
+		natras->internalFormat = GL_RGB5_A1;
 		natras->format = GL_RGBA;
-		natras->type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
+		natras->type = GL_UNSIGNED_SHORT_5_5_5_1;
 		natras->hasAlpha = 1;
+		natras->bbp = 2;
+		raster->depth = 16;
 		break;
 	default:
 		RWERROR((ERR_INVRASTER));
 		return nil;
 	}
 
+	raster->stride = raster->width*natras->bbp;
+
 	glGenTextures(1, &natras->texid);
 	glBindTexture(GL_TEXTURE_2D, natras->texid);
 	glTexImage2D(GL_TEXTURE_2D, 0, natras->internalFormat,
@@ -80,23 +87,22 @@ rasterCreateCameraTexture(Raster *raster)
 	Gl3Raster *natras = PLUGINOFFSET(Gl3Raster, raster, nativeRasterOffset);
 	switch(raster->format & 0xF00){
 	case Raster::C8888:
-		natras->internalFormat = GL_RGBA;
+		natras->internalFormat = GL_RGBA8;
 		natras->format = GL_RGBA;
 		natras->type = GL_UNSIGNED_BYTE;
 		natras->hasAlpha = 1;
 		break;
 	case Raster::C888:
 	default:
-		natras->internalFormat = GL_RGB;
+		natras->internalFormat = GL_RGB8;
 		natras->format = GL_RGB;
 		natras->type = GL_UNSIGNED_BYTE;
 		natras->hasAlpha = 0;
 		break;
 	case Raster::C1555:
-		// TODO: check if this is correct
-		natras->internalFormat = GL_RGBA;
+		natras->internalFormat = GL_RGB5_A1;
 		natras->format = GL_RGBA;
-		natras->type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
+		natras->type = GL_UNSIGNED_SHORT_5_5_5_1;
 		natras->hasAlpha = 1;
 		break;
 	}
@@ -136,6 +142,32 @@ rasterCreateZbuffer(Raster *raster)
 
 #endif
 
+/*
+{ 0, 0, 0 },
+{ 16, 4, GL_RGBA },	// 1555
+{ 16, 3, GL_RGB },	// 565
+{ 16, 4, GL_RGBA },	// 4444
+{ 0, 0, 0 },	// LUM8
+{ 32, 4, GL_RGBA },	// 8888
+{ 24, 3, GL_RGB },	// 888
+{ 16, 3, GL_RGB },	// D16
+{ 24, 3, GL_RGB },	// D24
+{ 32, 4, GL_RGBA },	// D32
+{ 16, 3, GL_RGB },	// 555
+
+0,
+GL_RGB5_A1,
+GL_RGB5,
+GL_RGBA4,
+0,
+GL_RGBA8,
+GL_RGB8,
+GL_RGB5,
+GL_RGB8,
+GL_RGBA8,
+GL_RGB5
+*/
+
 Raster*
 rasterCreate(Raster *raster)
 {
@@ -173,23 +205,59 @@ rasterCreate(Raster *raster)
 }
 
 uint8*
-rasterLock(Raster*, int32 level, int32 lockMode)
+rasterLock(Raster *raster, int32 level, int32 lockMode)
 {
-	printf("locking\n");
+#ifdef RW_OPENGL
+	Gl3Raster *natras = PLUGINOFFSET(Gl3Raster, raster, nativeRasterOffset);
+	uint8 *px;
+
+	assert(raster->privateFlags == 0);
+
+	px = (uint8*)rwMalloc(raster->stride*raster->height, 0);	// TODO: hint
+	assert(raster->pixels == nil);
+	raster->pixels = px;
+
+	if(lockMode & Raster::LOCKREAD || !(lockMode & Raster::LOCKNOFETCH)){
+		uint32 prev = bindTexture(natras->texid);
+		glGetTexImage(GL_TEXTURE_2D, level, natras->format, natras->type, px);
+		bindTexture(prev);
+	}
+
+	raster->privateFlags = lockMode;
+
+	return px;
+#else
 	return nil;
+#endif
 }
 
 void
-rasterUnlock(Raster*, int32)
+rasterUnlock(Raster *raster, int32 level)
 {
-	printf("unlocking\n");
+#ifdef RW_OPENGL
+	Gl3Raster *natras = PLUGINOFFSET(Gl3Raster, raster, nativeRasterOffset);
+
+	assert(raster->pixels);
+
+	if(raster->privateFlags & Raster::LOCKWRITE){
+		uint32 prev = bindTexture(natras->texid);
+		glTexImage2D(GL_TEXTURE_2D, level, natras->internalFormat,
+			     raster->width, raster->height,
+			     0, natras->format, natras->type, raster->pixels);
+		bindTexture(prev);
+	}
+
+	rwFree(raster->pixels);
+	raster->pixels = nil;
+	raster->privateFlags = 0;
+#endif
 }
 
 int32
 rasterNumLevels(Raster*)
 {
-	printf("numlevels\n");
-	return 0;
+	// TODO
+	return 1;
 }
 
 // Almost the same as d3d9 and ps2 function
@@ -242,23 +310,14 @@ imageFindRasterFormat(Image *img, int32 type,
 	return 1;
 }
 
-static uint8*
-flipImage(Image *image)
-{
-	int i;
-	uint8 *newPx = (uint8*)rwMalloc(image->stride*image->height, 0);
-	for(i = 0; i < image->height; i++)
-		memcpy(&newPx[i*image->stride], &image->pixels[(image->height-1-i)*image->stride], image->stride);
-	return newPx;
-}
-
 bool32
 rasterFromImage(Raster *raster, Image *image)
 {
 	if((raster->type&0xF) != Raster::TEXTURE)
 		return 0;
 
-#ifdef RW_OPENGL
+	void (*conv)(uint8 *out, uint8 *in) = nil;
+
 	// Unpalettize image if necessary but don't change original
 	Image *truecolimg = nil;
 	if(image->depth <= 8){
@@ -270,28 +329,27 @@ rasterFromImage(Raster *raster, Image *image)
 		image = truecolimg;
 	}
 
-	// NB: important to set the format of the input data here!
 	Gl3Raster *natras = PLUGINOFFSET(Gl3Raster, raster, nativeRasterOffset);
 	switch(image->depth){
 	case 32:
-		if(raster->format != Raster::C8888 &&
-		   raster->format != Raster::C888)
+		if(raster->format == Raster::C8888)
+			conv = conv_RGBA8888_to_RGBA8888;
+		else if(raster->format == Raster::C888)
+			conv = conv_RGB888_to_RGB888;
+		else
 			goto err;
-		natras->format = GL_RGBA;
-		natras->type = GL_UNSIGNED_BYTE;
-		natras->hasAlpha = 1;
 		break;
 	case 24:
-		if(raster->format != Raster::C888) goto err;
-		natras->format = GL_RGB;
-		natras->type = GL_UNSIGNED_BYTE;
-		natras->hasAlpha = 0;
+		if(raster->format == Raster::C888)
+			conv = conv_RGB888_to_RGB888;
+		else
+			goto err;
 		break;
 	case 16:
-		if(raster->format != Raster::C1555) goto err;
-		natras->format = GL_RGBA;
-		natras->type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
-		natras->hasAlpha = 1;
+		if(raster->format == Raster::C1555)
+			conv = conv_RGBA1555_to_RGBA5551;
+		else
+			goto err;
 		break;
 
 	case 8:
@@ -304,17 +362,26 @@ rasterFromImage(Raster *raster, Image *image)
 
 	natras->hasAlpha = image->hasAlpha();
 
+	uint8 *pixels = raster->lock(0, Raster::LOCKWRITE|Raster::LOCKNOFETCH);
+	assert(pixels);
+	uint8 *imgpixels = image->pixels + (image->height-1)*image->stride;
 
-	uint8 *flipped = flipImage(image);
+	int x, y;
+	assert(image->width == raster->width);
+	assert(image->height == raster->height);
+	for(y = 0; y < image->height; y++){
+		uint8 *imgrow = imgpixels;
+		uint8 *rasrow = pixels;
+		for(x = 0; x < image->width; x++){
+			conv(rasrow, imgrow);
+			imgrow += image->bpp;
+			rasrow += natras->bbp;
+		}
+		imgpixels -= image->stride;
+		pixels += raster->stride;
+	}
 
-	glBindTexture(GL_TEXTURE_2D, natras->texid);
-	glTexImage2D(GL_TEXTURE_2D, 0, natras->internalFormat,
-	             raster->width, raster->height,
-	             0, natras->format, natras->type, flipped);
-	glBindTexture(GL_TEXTURE_2D, 0);
-
-	rwFree(flipped);
-#endif
+	raster->unlock(0);
 	return 1;
 }
 
@@ -327,10 +394,12 @@ createNativeRaster(void *object, int32 offset, int32)
 }
 
 static void*
-destroyNativeRaster(void *object, int32, int32)
+destroyNativeRaster(void *object, int32 offset, int32)
 {
-	//Gl3Raster *ras = PLUGINOFFSET(Gl3Raster, object, offset);
-	// TODO
+	Gl3Raster *ras = PLUGINOFFSET(Gl3Raster, object, offset);
+#ifdef RW_OPENGL
+	glDeleteTextures(1, &ras->texid);
+#endif
 	return object;
 }
 
@@ -342,6 +411,113 @@ copyNativeRaster(void *dst, void *, int32 offset, int32)
 	return dst;
 }
 
+static uint32
+getLevelSize(Raster *raster, int32 level)
+{
+	Gl3Raster *natras = PLUGINOFFSET(Gl3Raster, raster, nativeRasterOffset);
+	uint32 size = raster->stride*raster->height;
+	while(level--)
+		size /= 4;
+	return size;
+}
+
+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_GL3){
+		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();
+	int32 width = stream->readI32();
+	int32 height = stream->readI32();
+	int32 depth = stream->readI32();
+	int32 numLevels = stream->readI32();
+
+	Raster *raster;
+	Gl3Raster *natras;
+	raster = Raster::create(width, height, depth, format | Raster::TEXTURE, PLATFORM_GL3);
+	assert(raster);
+	natras = PLUGINOFFSET(Gl3Raster, raster, nativeRasterOffset);
+	tex->raster = raster;
+
+	uint32 size;
+	uint8 *data;
+	for(int32 i = 0; i < numLevels; i++){
+		size = stream->readU32();
+		if(i < raster->getNumLevels()){
+			data = raster->lock(i, Raster::LOCKWRITE|Raster::LOCKNOFETCH);
+			stream->read(data, size);
+			raster->unlock(i);
+		}else
+			stream->seek(size);
+	}
+	return tex;
+}
+
+void
+writeNativeTexture(Texture *tex, Stream *stream)
+{
+	Raster *raster = tex->raster;
+	Gl3Raster *natras = PLUGINOFFSET(Gl3Raster, raster, nativeRasterOffset);
+
+	int32 chunksize = getSizeNativeTexture(tex);
+	writeChunkHeader(stream, ID_STRUCT, chunksize-12);
+	stream->writeU32(PLATFORM_GL3);
+
+	// Texture
+	stream->writeU32(tex->filterAddressing);
+	stream->write(tex->name, 32);
+	stream->write(tex->mask, 32);
+
+	// Raster
+	int32 numLevels = raster->getNumLevels();
+	stream->writeI32(raster->format);
+	stream->writeI32(raster->width);
+	stream->writeI32(raster->height);
+	stream->writeI32(raster->depth);
+	stream->writeI32(numLevels);
+	// TODO: compression? auto mipmaps?
+
+	uint32 size;
+	uint8 *data;
+	for(int32 i = 0; i < numLevels; i++){
+		size = getLevelSize(raster, i);
+		stream->writeU32(size);
+		data = raster->lock(i, Raster::LOCKREAD);
+		stream->write(data, size);
+		raster->unlock(i);
+	}
+}
+
+uint32
+getSizeNativeTexture(Texture *tex)
+{
+	uint32 size = 12 + 72 + 20;
+	int32 levels = tex->raster->getNumLevels();
+	for(int32 i = 0; i < levels; i++)
+		size += 4 + getLevelSize(tex->raster, i);
+	return size;
+}
+
+
+
 void registerNativeRaster(void)
 {
 	nativeRasterOffset = Raster::registerPlugin(sizeof(Gl3Raster),
diff --git a/src/gl/rwgl3.h b/src/gl/rwgl3.h
index 91e462c..2384ee7 100644
--- a/src/gl/rwgl3.h
+++ b/src/gl/rwgl3.h
@@ -171,6 +171,10 @@ int32 setLights(WorldLights *lightData);
 // per Mesh
 void setTexture(int32 n, Texture *tex);
 
+
+uint32 bindTexture(uint32 texid);
+
+
 void flushCache(void);
 
 #endif
@@ -202,6 +206,7 @@ struct Gl3Raster
 	int32 internalFormat;
 	int32 type;
 	int32 format;
+	int32 bbp;	// bytes per pixel
 	// texture object
 	uint32 texid;
 
@@ -212,6 +217,10 @@ struct Gl3Raster
 	uint8 addressV;
 };
 
+Texture *readNativeTexture(Stream *stream);
+void writeNativeTexture(Texture *tex, Stream *stream);
+uint32 getSizeNativeTexture(Texture *tex);
+
 void registerNativeRaster(void);
 
 }
diff --git a/src/gl/shaders/header.vert b/src/gl/shaders/header.vert
index 8048156..20c5097 100644
--- a/src/gl/shaders/header.vert
+++ b/src/gl/shaders/header.vert
@@ -84,5 +84,5 @@ float DoFog(float w)
 }
 
 #define DIRECTIONALS
-//#define POINTLIGHTS
-//#define SPOTLIGHTS
\ No newline at end of file
+#define POINTLIGHTS
+#define SPOTLIGHTS
\ No newline at end of file
diff --git a/src/gl/shaders/header_vs.inc b/src/gl/shaders/header_vs.inc
index 2f884dc..b127335 100644
--- a/src/gl/shaders/header_vs.inc
+++ b/src/gl/shaders/header_vs.inc
@@ -85,5 +85,5 @@ const char *header_vert_src =
 "}\n"
 
 "#define DIRECTIONALS\n"
-"//#define POINTLIGHTS\n"
-"//#define SPOTLIGHTS\n";
+"#define POINTLIGHTS\n"
+"#define SPOTLIGHTS\n";
diff --git a/src/raster.cpp b/src/raster.cpp
index bee0c89..9dfe47a 100644
--- a/src/raster.cpp
+++ b/src/raster.cpp
@@ -215,5 +215,42 @@ Raster::renderFast(int32 x, int32 y)
 	return engine->device.rasterRenderFast(this,x, y);
 }
 
+void
+conv_RGBA8888_to_RGBA8888(uint8 *out, uint8 *in)
+{
+	out[0] = in[0];
+	out[1] = in[1];
+	out[2] = in[2];
+	out[3] = in[3];
+}
+
+void
+conv_RGB888_to_RGBA8888(uint8 *out, uint8 *in)
+{
+	out[0] = in[0];
+	out[1] = in[1];
+	out[2] = in[2];
+	out[3] = 0xFF;
+}
+
+void
+conv_RGB888_to_RGB888(uint8 *out, uint8 *in)
+{
+	out[0] = in[0];
+	out[1] = in[1];
+	out[2] = in[2];
+}
+
+void
+conv_RGBA1555_to_RGBA5551(uint8 *out, uint8 *in)
+{
+	uint32 r, g, b, a;
+	a = (in[1]>>7) & 1;
+	r = (in[1]>>2) & 0x1F;
+	g = (in[1]&3)<<3 | (in[0]>>5)&7;
+	b = in[0] & 0x1F;
+	out[0] = a | b<<1 | g<<6;
+	out[1] = g>>2 | r<<3;
+}
 
 }
diff --git a/src/rwobjects.h b/src/rwobjects.h
index b8a1b5b..9e64a64 100644
--- a/src/rwobjects.h
+++ b/src/rwobjects.h
@@ -251,6 +251,11 @@ struct Raster
 	};
 };
 
+void conv_RGBA8888_to_RGBA8888(uint8 *out, uint8 *in);
+void conv_RGB888_to_RGBA8888(uint8 *out, uint8 *in);
+void conv_RGB888_to_RGB888(uint8 *out, uint8 *in);
+void conv_RGBA1555_to_RGBA5551(uint8 *out, uint8 *in);
+
 
 #define IGNORERASTERIMP 0
 
diff --git a/src/texture.cpp b/src/texture.cpp
index fe52097..4fb3dcc 100644
--- a/src/texture.cpp
+++ b/src/texture.cpp
@@ -14,6 +14,7 @@
 #include "d3d/rwxbox.h"
 #include "d3d/rwd3d8.h"
 #include "d3d/rwd3d9.h"
+#include "gl/rwgl3.h"
 
 #define PLUGIN_ID 0
 
@@ -455,6 +456,8 @@ Texture::streamReadNative(Stream *stream)
 		return d3d9::readNativeTexture(stream);
 	if(platform == PLATFORM_XBOX)
 		return xbox::readNativeTexture(stream);
+	if(platform == PLATFORM_GL3)
+		return gl3::readNativeTexture(stream);
 	assert(0 && "unsupported platform");
 	return nil;
 }
@@ -470,6 +473,8 @@ Texture::streamWriteNative(Stream *stream)
 		d3d9::writeNativeTexture(this, stream);
 	else if(this->raster->platform == PLATFORM_XBOX)
 		xbox::writeNativeTexture(this, stream);
+	else if(this->raster->platform == PLATFORM_GL3)
+		gl3::writeNativeTexture(this, stream);
 	else
 		assert(0 && "unsupported platform");
 }
@@ -485,6 +490,8 @@ Texture::streamGetSizeNative(void)
 		return d3d9::getSizeNativeTexture(this);
 	if(this->raster->platform == PLATFORM_XBOX)
 		return xbox::getSizeNativeTexture(this);
+	if(this->raster->platform == PLATFORM_GL3)
+		return gl3::getSizeNativeTexture(this);
 	assert(0 && "unsupported platform");
 	return 0;
 }