Jump to content

Ultralight Implementation in Leadwerks


martyj
 Share

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?

Link to comment
Share on other sites

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();
	}
  

 

Link to comment
Share on other sites

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();
	}
}

 

Link to comment
Share on other sites

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;

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...