Jump to content
martyj

Ultralight Implementation in Leadwerks

Recommended Posts

I'm trying to implement Ultralight into Leadwerks and I"m struggling to find the best way to do it.

I can see to "easy" options.

1. Create a GPUDriver instance for Ultralight which utilizes Leadwerks rendering. (Requires my own custom 2D shader)
2. Use the OpenGL implementation for Ultralight but use a separate thread for rendering the browser and writing the image data to a shared buffer

#1 seems very difficult and error prone as I have to provide my own shader and support for this rendering.

#2 seems easier but I don't know quite the best way to do this.

Would something like this work for #2?
 

DWORD WINAPI MyThreadFunction(LPVOID lpParam)
{
	System::Print("THread2");
	HDC hdc = CreateCompatibleDC(0);

	HGLRC glrc = wglCreateContext(hdc);
	wglMakeCurrent(window->hdc, glrc);

	unsigned char buff[512 * 512 * 3];
	while (true)
	{
		drawUI();
		
		dumpToBuffer(512, 512);
	}

	wglDeleteContext(glrc);
}

 

What would you guys recommend?

Share this post


Link to post

Would this be useful?

Window* Window::Create(HWND hwnd)

 

Share this post


Link to post

This is the approach I am taking. It doesn't work right now, but it should pass along the idea for #2.


We would have two threads.

Thread 1 would be the game in itself.
Thread 2 would be the HTML render thread that uses OpenGL implementation for Ultralight UI

Is this a good approach?
 

const GLchar* vertexShader = R"EOF(
#version 140

in  vec3 in_Position;
in  vec3 in_Color;
out vec3 ex_Color;

void main(void)
{
	gl_Position = vec4(in_Position, 1.0);
	ex_Color = in_Color;
}

)EOF";

const GLchar* fragShader = R"EOF(
#version 140

precision highp float; // needed only for version 1.30

in  vec3 ex_Color;
out vec4 out_Color;

void main(void)
{
	out_Color = vec4(ex_Color,1.0);
}

)EOF";

unsigned int vertexArrayObjID[1];
unsigned int vertexBufferObjID[2];

void init(void)
{
	/* set viewing projection */
	glMatrixMode(GL_PROJECTION);
	glFrustum(-0.5F, 0.5F, -0.5F, 0.5F, 1.0F, 3.0F);

	/* position viewer */
	glMatrixMode(GL_MODELVIEW);
	glTranslatef(0.0F, 0.0F, -2.0F);

	int vertSize = 9;
	int colorSize = 9;
	GLfloat vertData[]{
		-0.3, 0.5, -1.0,
		-0.8, -0.5, -1.0,
		0.2, -0.5, -1.0
	};

	GLfloat colorData[]{
		1.0, 0.0, 0.0,
		0.0, 1.0, 0.0,
		0.0, 0.0, 1.0
	};

	// Allocate Vertex Array Objects
	glGenVertexArrays(1, &vertexArrayObjID[0]);
	// Setup first Vertex Array Object
	glBindVertexArray(vertexArrayObjID[0]);
	glGenBuffers(2, vertexBufferObjID);

	// VBO for vertex data
	glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObjID[0]);
	glBufferData(GL_ARRAY_BUFFER, vertSize * sizeof(GLfloat), vertData, GL_STATIC_DRAW);
	glVertexAttribPointer((GLuint)0, 3, GL_FLOAT, GL_FALSE, 0, 0);
	glEnableVertexAttribArray(0);

	// VBO for colour data
	glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObjID[1]);
	glBufferData(GL_ARRAY_BUFFER, colorSize * sizeof(GLfloat), colorData, GL_STATIC_DRAW);
	glVertexAttribPointer((GLuint)1, 3, GL_FLOAT, GL_FALSE, 0, 0);
	glEnableVertexAttribArray(1);

	glBindVertexArray(0);

	GLuint renderBuffer[1];
	glGenRenderbuffersEXT(1, renderBuffer);

	glBindRenderbufferEXT(GL_FRAMEBUFFER_EXT, renderBuffer[0]);

	glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGB, 512, 512);

}

void initShaders(void)
{
	GLuint glProgram;

	GLuint vertShaderPtr = glCreateShader(GL_VERTEX_SHADER);
	GLuint fragShaderPtr = glCreateShader(GL_FRAGMENT_SHADER);
	
	glShaderSource(vertShaderPtr, 1, &vertexShader, 0);
	glShaderSource(fragShaderPtr, 1, &fragShader, 0);

	GLint compiled;
	glCompileShader(vertShaderPtr);
	glGetShaderiv(vertShaderPtr, GL_COMPILE_STATUS, &compiled);
	if (!compiled)
	{
		cout << "Vertex shader not compiled." << endl;
	}

	glCompileShader(fragShaderPtr);
	glGetShaderiv(fragShaderPtr, GL_COMPILE_STATUS, &compiled);
	if (!compiled)
	{
		cout << "Fragment shader not compiled." << endl;
	}

	glProgram = glCreateProgram();

	glBindAttribLocation(glProgram, 0, "in_Position");
	glBindAttribLocation(glProgram, 1, "in_Color");

	glAttachShader(glProgram, vertShaderPtr);
	glAttachShader(glProgram, fragShaderPtr);

	glLinkProgram(glProgram);
	glUseProgram(glProgram);
}

void display(void)
{
	// clear the screen
	glClear(GL_COLOR_BUFFER_BIT);

	glBindVertexArray(vertexArrayObjID[0]);	// First VAO
	glDrawArrays(GL_TRIANGLES, 0, 3);	// draw first object

	glBindVertexArray(0);
}

void screendump(int W, int H)
{
	FILE* out = fopen("screenshot.tga", "wb");
	char* pixel_data = new char[3 * W*H];
	short TGAhead[] = { 0, 2, 0, 0, 0, 0, W, H, 24 };

	glReadBuffer(GL_RENDERBUFFER_EXT);
	glReadPixels(0, 0, W, H, GL_BGR, GL_UNSIGNED_BYTE, pixel_data);
// TODO: Render to texture instead. Use texture with Context::drawImage
	fwrite(&TGAhead, sizeof(TGAhead), 1, out);
	fwrite(pixel_data, 3 * W*H, 1, out);
	fclose(out);

	delete[] pixel_data;
}

static Window* window;
DWORD WINAPI MyThreadFunction(LPVOID lpParam)
{
	System::Print("THread2");
	HDC hdc = CreateCompatibleDC(0);

	HGLRC glrc = wglCreateContext(hdc);
	wglMakeCurrent(window->hdc, glrc);

	init();
	initShaders();

	int w = 512;
	int h = 512;
	glViewport(0, 0, (GLsizei)w, (GLsizei)h);
	while (true)
	{
		display();
		
		SwapBuffers(window->hdc);
		//screendump(w, h);
	}

	wglDeleteContext(glrc);
}


int main(int argc,const char *argv[])
{
  // Leadwerks Stuff
  // ...
  // Game Setup
window = Leadwerks::Window::Create();
	Context* context = Context::Create(window);
	World* world = World::Create();
	Light* light = DirectionalLight::Create();
	light->SetRotation(35, 35, 0);

	//Create the ground
	Model* ground = Model::Box(10, 1, 10);
	ground->SetPosition(0, -0.5, 0);
	ground->SetColor(0.0, 1.0, 0.0);

	//Create a shape
	Shape* shape = Shape::Box(0, 0, 0, 0, 0, 0, 10, 1, 10);
	ground->SetShape(shape);
	shape->Release();

	//Create a character
	Pivot* player = Pivot::Create();
	Entity* visiblecapsule = Model::Cylinder(16, player);
	visiblecapsule->SetScale(1, 2, 1);
	visiblecapsule->SetPosition(0, 1, 0);
	player->SetMass(1);
	player->SetPhysicsMode(Entity::CharacterPhysics);

	Camera* camera = Camera::Create();
	camera->SetPosition(Vec3(0, 1.5, 0), false);
	camera->SetParent(player);

	DWORD threadId;
	HANDLE thread = CreateThread(NULL, 0, MyThreadFunction, nullptr, 0, &threadId);
	System::Print("THread1");
	while (true)
	{
		if (window->Closed() || window->KeyDown(Key::Escape)) return false;

		Leadwerks::Time::Update();

		float move = (window->KeyDown(Key::W) - window->KeyDown(Key::S)) * 4;
		float strafe = (window->KeyDown(Key::D) - window->KeyDown(Key::A)) * 4;
		player->SetInput(0, move, strafe);

		world->Update();
		world->Render();
		context->Sync();
	}
  

 

Share this post


Link to post

I have no idea. This is some crazy stuff.

Share this post


Link to post

This post is for future reference. I don't have it working with a 2nd off screen context. I have two threads rendering two scenes to two windows.

I think my next steps is to ditch SDL and use Frame Buffer Objects.
https://www.khronos.org/opengl/wiki/Framebuffer_Object

I'll update people on my progress.

 


#include <Leadwerks.h>

#include <SDL.h>

using namespace Leadwerks;


#include <GL/glut.h>

GLfloat light_diffuse[] = { 1.0, 0.0, 0.0, 1.0 };  /* Red diffuse light. */
GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };  /* Infinite light location. */
GLfloat n[6][3] = {  /* Normals for the 6 faces of a cube. */
  {-1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {1.0, 0.0, 0.0},
  {0.0, -1.0, 0.0}, {0.0, 0.0, 1.0}, {0.0, 0.0, -1.0} };
GLint faces[6][4] = {  /* Vertex indices for the 6 faces of a cube. */
  {0, 1, 2, 3}, {3, 2, 6, 7}, {7, 6, 5, 4},
  {4, 5, 1, 0}, {5, 6, 2, 1}, {7, 4, 0, 3} };
GLfloat v[8][3];  /* Will be filled in with X,Y,Z vertexes. */

void drawBox(void)
{
	int i;

	for (i = 0; i < 6; i++) {
		glBegin(GL_QUADS);
		glNormal3fv(&n[i][0]);
		glVertex3fv(&v[faces[i][0]][0]);
		glVertex3fv(&v[faces[i][1]][0]);
		glVertex3fv(&v[faces[i][2]][0]);
		glVertex3fv(&v[faces[i][3]][0]);
		glEnd();
	}
}

void screendump(int W, int H)
{
	FILE* out = fopen("screenshot.tga", "wb");
	char* pixel_data = new char[3 * W*H];
	short TGAhead[] = { 0, 2, 0, 0, 0, 0, W, H, 24 };

	glReadBuffer(GL_RENDERBUFFER_EXT);
	glReadPixels(0, 0, W, H, GL_BGR, GL_UNSIGNED_BYTE, pixel_data);
	// TODO: Render to texture instead. Use texture with Context::drawImage
	fwrite(&TGAhead, sizeof(TGAhead), 1, out);
	fwrite(pixel_data, 3 * W*H, 1, out);
	fclose(out);

	delete[] pixel_data;
}

void init(void)
{
	/* Setup cube vertex data. */
	v[0][0] = v[1][0] = v[2][0] = v[3][0] = -1;
	v[4][0] = v[5][0] = v[6][0] = v[7][0] = 1;
	v[0][1] = v[1][1] = v[4][1] = v[5][1] = -1;
	v[2][1] = v[3][1] = v[6][1] = v[7][1] = 1;
	v[0][2] = v[3][2] = v[4][2] = v[7][2] = 1;
	v[1][2] = v[2][2] = v[5][2] = v[6][2] = -1;

	/* Enable a single OpenGL light. */
	glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
	glLightfv(GL_LIGHT0, GL_POSITION, light_position);
	glEnable(GL_LIGHT0);
	glEnable(GL_LIGHTING);

	/* Use depth buffering for hidden surface elimination. */
	glEnable(GL_DEPTH_TEST);

	/* Setup the view of the cube. */
	glMatrixMode(GL_PROJECTION);
	gluPerspective( /* field of view in degree */ 40.0,
		/* aspect ratio */ 1.0,
		/* Z near */ 1.0, /* Z far */ 10.0);
	glMatrixMode(GL_MODELVIEW);
	gluLookAt(0.0, 0.0, 5.0,  /* eye is at (0,0,5) */
		0.0, 0.0, 0.0,      /* center is at (0,0,0) */
		0.0, 1.0, 0.);      /* up is in positive Y direction */

	  /* Adjust cube position to be asthetic angle. */
	glTranslatef(0.0, 0.0, -1.0);
	glRotatef(60, 1.0, 0.0, 0.0);
	glRotatef(-20, 0.0, 0.0, 1.0);
}


int webBrowserMain()
{
	// Window mode MUST include SDL_WINDOW_OPENGL for use with OpenGL.
	SDL_Window *window = SDL_CreateWindow("Browser", 0, 0, 512, 512, SDL_WINDOW_OPENGL);

	// Create an OpenGL context associated with the window.
	SDL_GLContext glcontext = SDL_GL_CreateContext(window);

	init();

	while (true)
	{
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		drawBox();

		SDL_GL_SwapWindow(window);
	}
	SDL_GL_DeleteContext(glcontext);

	return 0; 
}

DWORD WINAPI MyThreadFunction(LPVOID lpParam)
{
	return webBrowserMain();
}

#define GLUT
int main(int argc, char *argv[])
{
	DWORD threadId = 0;
	CreateThread(NULL, 0, MyThreadFunction, nullptr, 0, &threadId);

	Window* window = Leadwerks::Window::Create();
	Context* context = Context::Create(window);
	World* world = World::Create();
	Light* light = DirectionalLight::Create();
	light->SetRotation(35, 35, 0);

	//Create the ground
	Model* ground = Model::Box(10, 1, 10);
	ground->SetPosition(0, -0.5, 0);
	ground->SetColor(0.0, 1.0, 0.0);

	//Create a shape
	Shape* shape = Shape::Box(0, 0, 0, 0, 0, 0, 10, 1, 10);
	ground->SetShape(shape);
	shape->Release();

	//Create a character
	Pivot* player = Pivot::Create();
	Entity* visiblecapsule = Model::Cylinder(16, player);
	visiblecapsule->SetScale(1, 2, 1);
	visiblecapsule->SetPosition(0, 1, 0);
	player->SetMass(1);
	player->SetPhysicsMode(Entity::CharacterPhysics);

	Camera* camera = Camera::Create();
	camera->SetPosition(Vec3(0, 1.5, 0), false);
	camera->SetParent(player);

	while (true)
	{
		if (window->Closed() || window->KeyDown(Key::Escape)) return false;

		Leadwerks::Time::Update();

		float move = (window->KeyDown(Key::W) - window->KeyDown(Key::S)) * 4;
		float strafe = (window->KeyDown(Key::D) - window->KeyDown(Key::A)) * 4;
		player->SetInput(0, move, strafe);

		world->Update();
		world->Render();
		context->Sync();
	}
}

 

Share this post


Link to post

FYI, the OpenGLBuffer class provides a way to access FBOs. They are not easy. You will need to look in the header file to get the FBO ID.

  • Like 1

Share this post


Link to post

So I have it rendering to a file/texture in a background thread.

What I'd like to do now is to use wglShareLists to have textures synced between the Leadwerks Context and the SDL_Context to render a GL_TEXTURE_2D.

What's the best way to create a Leadwerks::Texture from a GLuint? If I don't have to call glReadPixels to render a texture, it will probably help with performance.

 

I assume if I set these values it will work to call context->DrawImage?

        int filtermode -> Pixel;
        int format -> RGBA8;
        int target -> Texture2D;
        bool hasmipmaps -> false;
        static float anisotropy -> What goes here?
        int bindindex -> output from glGenTextures;
        int width -> FBO Width;
        int height -> FBO height;
        int depth -> 0;
        bool clamp[3] -> What goes here?
        int currentmiplevel -> What goes here?
        int samples -> 0;

Share this post


Link to post
  • For anisotropy you can just put 1.0, but I don't think it will even be used.
  • clamp[3] should be {false,false,false}
  • curentmiplevel should be 0.
  • Like 1

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Create Your Account

Sign in

Already have an account? Sign in here.

Sign In Now
×