diff --git a/include/Camera.h b/include/Camera.h index da6f6e1..cb84706 100644 --- a/include/Camera.h +++ b/include/Camera.h @@ -1,6 +1,6 @@ #pragma once #include -#include +#include #include "util.hpp" #include "Pub_Sub.h" @@ -10,7 +10,7 @@ public: enum DIRECTION { FORWARD, REARWARD, LEFT, RIGHT, UP, DOWN }; Camera(); - Camera(sf::Vector3f position, sf::Vector2f direction); + Camera(sf::Vector3f position, sf::Vector2f direction, sf::RenderWindow *window); ~Camera(); int set_position(sf::Vector3f position); @@ -50,5 +50,12 @@ private: // These are spherical coords sf::Vector2f direction; + + bool mouse_enabled = true; + sf::Vector2i deltas; + sf::Vector2i fixed; + sf::Vector2i prev_pos; + + sf::RenderWindow *window; }; diff --git a/include/Hardware_Caster.h b/include/Hardware_Caster.h index 2b8bc09..5a5c863 100644 --- a/include/Hardware_Caster.h +++ b/include/Hardware_Caster.h @@ -50,7 +50,7 @@ public: // Both will create the view matrix, view res buffer void create_viewport(int width, int height, float v_fov, float h_fov) override; - void assign_lights(std::vector lights) override; + void assign_lights(std::vector *lights) override; void assign_map(Old_Map *map) override; void assign_camera(Camera *camera) override; void validate() override; @@ -60,6 +60,8 @@ public: void compute() override; void draw(sf::RenderWindow* window) override; + + void test_edit_viewport(int width, int height, float v_fov, float h_fov); private: diff --git a/include/RayCaster.h b/include/RayCaster.h index e3f9d44..17326ff 100644 --- a/include/RayCaster.h +++ b/include/RayCaster.h @@ -24,7 +24,7 @@ public: virtual void assign_map(Old_Map *map) = 0; virtual void assign_camera(Camera *camera) = 0; virtual void create_viewport(int width, int height, float v_fov, float h_fov) = 0; - virtual void assign_lights(std::vector lights) = 0; + virtual void assign_lights(std::vector *lights) = 0; virtual void validate() = 0; // draw will abstract the gl sharing and software rendering @@ -39,7 +39,7 @@ protected: Old_Map * map = nullptr; Camera *camera = nullptr; - std::vector lights; + std::vector *lights; int light_count = 0; sf::Uint8 *viewport_image = nullptr; sf::Vector4f *viewport_matrix = nullptr; diff --git a/include/Software_Caster.h b/include/Software_Caster.h index 110f6d8..f4d03e8 100644 --- a/include/Software_Caster.h +++ b/include/Software_Caster.h @@ -15,7 +15,7 @@ public: // Both will create the view matrix, view res buffer void create_viewport(int width, int height, float v_fov, float h_fov) override; - void assign_lights(std::vector lights) override; + void assign_lights(std::vector *lights) override; void assign_map(Old_Map *map) override; void assign_camera(Camera *camera) override; void validate() override; diff --git a/kernels/ray_caster_kernel.cl b/kernels/ray_caster_kernel.cl index a2cc7b2..40f974c 100644 --- a/kernels/ray_caster_kernel.cl +++ b/kernels/ray_caster_kernel.cl @@ -5,7 +5,7 @@ float4 white_light(float4 input, float3 light, int3 mask) { input.w = input.w + acos( dot( normalize(light), - normalize(fabs(convert_float3(mask))) + normalize(convert_float3(mask * (-mask))) ) ) / 2; @@ -13,6 +13,31 @@ float4 white_light(float4 input, float3 light, int3 mask) { } + +float4 view_light(float4 in_color, float3 light, float3 view, int3 mask) { + + float diffuse = max(dot(normalize(convert_float3(mask)), normalize(light)), 0.0f); + if (dot(light, normalize(convert_float3(mask))) > 0.0) + { + float3 halfwayVector = normalize(normalize(light) + normalize(view)); + float specTmp = max(dot(normalize(convert_float3(mask)), halfwayVector), 0.0f); + return in_color + pow(specTmp, 1.0f) * 0.01 +diffuse * 0.5; + } + + //float3 halfwayDir = normalize(normalize(view) + normalize(light)); + //float spec = pow(max(dot(normalize(convert_float3(mask)), halfwayDir), 0.0f), 32.0f); + in_color.w += 0.2; + return in_color; +} + + +void cast_ray(float3 ray_origin, float3 ray_direction) { + +} + + + + // 0 1 2 3 4 5 6 7 8 9 // {r, g, b, i, x, y, z, x', y', z'} @@ -99,21 +124,21 @@ __kernel void raycaster( global int* seed_memory ){ - // Get and set the random seed from seed memory int global_id = get_global_id(0); + + // Get and set the random seed from seed memory int seed = seed_memory[global_id]; int random_number = rand(&seed); seed_memory[global_id] = seed; - - size_t id = get_global_id(0); - int2 pixel = {id % (*resolution).x, id / (*resolution).x}; + // Get the pixel on the viewport, and find the view matrix ray that matches it + int2 pixel = { global_id % (*resolution).x, global_id / (*resolution).x}; float3 ray_dir = projection_matrix[pixel.x + (*resolution).x * pixel.y]; - if (pixel.x == 960 && pixel.y == 540) { - write_imagef(image, pixel, (float4)(0.00, 1.00, 0.00, 1.00)); - return; - } + //if (pixel.x == 960 && pixel.y == 540) { + // write_imagef(image, pixel, (float4)(0.00, 1.00, 0.00, 1.00)); + // return; + //} // Pitch ray_dir = (float3)( @@ -160,67 +185,53 @@ __kernel void raycaster( intersection_t.z += delta_t.z; } - // use a ghetto ass rng to give rays a "fog" appearance - int2 randoms = { random_number, 14 }; - uint tseed = randoms.x + id; - uint t = tseed ^ (tseed << 11); - uint result = randoms.y ^ (randoms.y >> 19) ^ (t ^ (t >> 8)); - - int max_dist = 800 + result % 100; + // Hard cut-off for how far the ray can travel + int max_dist = 800; int dist = 0; - int3 mask = { 0, 0, 0 }; - float4 color = { 0.73, 0.81, 0.89, 0.6 }; - float4 c = (float4)(0.60, 0.00, 0.40, 0.1); - c.x += (result % 100) / 10; + + int3 face_mask = { 0, 0, 0 }; + float4 fog_color = { 0.73, 0.81, 0.89, 0.8 }; + float4 voxel_color = (float4)(0.25, 0.52, 0.30, 0.1); + float4 overshoot_color = { 0.25, 0.48, 0.52, 0.8 }; // Andrew Woo's raycasting algo do { - mask = intersection_t.xyz <= min(intersection_t.yzx, intersection_t.zxy); - intersection_t += delta_t * fabs(convert_float3(mask.xyz)); - voxel.xyz += voxel_step.xyz * mask.xyz; + // Fancy no branch version of the logic step + face_mask = intersection_t.xyz <= min(intersection_t.yzx, intersection_t.zxy); + intersection_t += delta_t * fabs(convert_float3(face_mask.xyz)); + voxel.xyz += voxel_step.xyz * face_mask.xyz; // If the ray went out of bounds int3 overshoot = voxel <= *map_dim; int3 undershoot = voxel > 0; if (overshoot.x == 0 || overshoot.y == 0 || overshoot.z == 0 || undershoot.x == 0 || undershoot.y == 0){ - write_imagef(image, pixel, white_light(mix(color, (float4)(0.40, 0.00, 0.40, 0.2), 1.0 - max(dist / 700.0f, (float)0)), (float3)(lights[7], lights[8], lights[9]), mask)); + write_imagef(image, pixel, white_light(mix(fog_color, overshoot_color, 1.0 - max(dist / 700.0f, (float)0)), (float3)(lights[7], lights[8], lights[9]), face_mask)); return; } if (undershoot.z == 0) { - write_imagef(image, pixel, white_light(mix(color, (float4)(0.40, 0.00, 0.40, 0.2), 1.0 - max(dist / 700.0f, (float)0)), (float3)(lights[7], lights[8], lights[9]), mask)); + write_imagef(image, pixel, white_light(mix(fog_color, overshoot_color, 1.0 - max(dist / 700.0f, (float)0)), (float3)(lights[7], lights[8], lights[9]), face_mask)); return; } // If we hit a voxel - //int index = voxel.x * (*map_dim).y * (*map_dim).z + voxel.z * (*map_dim).z + voxel.y; int index = voxel.x + (*map_dim).x * (voxel.y + (*map_dim).z * (voxel.z)); int voxel_data = map[index]; if (voxel_data != 0) { switch (voxel_data) { - case 1: - write_imagef(image, pixel, (float4)(.50, .00, .00, 1)); - return; - case 2: - write_imagef(image, pixel, (float4)(.00, .50, .40, 1.00)); - return; - case 3: - write_imagef(image, pixel, (float4)(.00, .00, .50, 1.00)); - return; - case 4: - write_imagef(image, pixel, (float4)(.25, .00, .25, 1.00)); - return; + case 5: //write_imagef(image, pixel, (float4)(0.40, 0.00, 0.40, 0.2)); - write_imagef(image, pixel, white_light(mix(color, c, 1.0 - max((dist/700.0f) - 0.3f, (float)0)), (float3)(lights[7], lights[8], lights[9]), mask)); + write_imagef(image, pixel, view_light(voxel_color, (convert_float3(voxel) + offset) - (float3)(lights[4], lights[5], lights[6]), (convert_float3(voxel) + offset) - (*cam_pos), face_mask)); + //write_imagef(image, pixel, white_light(mix(fog_color, voxel_color, 1.0 - max((dist/700.0f) - 0.3f, (float)0)), (float3)(lights[7], lights[8], lights[9]), face_mask)); return; float3 vox = convert_float3(voxel); - float3 norm = normalize(convert_float3(mask) * convert_float3(voxel_step)); + float3 norm = normalize(convert_float3(face_mask) * convert_float3(voxel_step)); float4 color = (float4)(0.95, 0.00, 0.25, 1.00); @@ -235,12 +246,16 @@ __kernel void raycaster( )); return; - + + + case 6: - write_imagef(image, pixel, (float4)(.30, .80, .10, 1.00)); + write_imagef(image, pixel, view_light((float4)(0.0, 0.239, 0.419, 0.3), (convert_float3(voxel) + offset) - (float3)(lights[4], lights[5], lights[6]), (convert_float3(voxel) + offset) - (*cam_pos), face_mask)); + //write_imagef(image, pixel, white_light(mix((float4)(0.73, 0.81, 0.89, 0.6), (float4)(0.0, 0.239, 0.419, 0.3), 1.0 - max((dist / 700.0f) - 0.3f, (float)0)), (float3)(lights[7], lights[8], lights[9]), face_mask)); return; + default: - //write_imagef(image, pixel, (float4)(.30, .10, .10, 1.00)); + write_imagef(image, pixel, (float4)(.30, .10, .10, 1.00)); continue; } } @@ -249,7 +264,7 @@ __kernel void raycaster( } while (dist / 700.0f < 1); //dist < max_dist - write_imagef(image, pixel, white_light(mix(color, (float4)(0.40, 0.00, 0.40, 0.2), 1.0 - max(dist / 700.0f, (float)0)), (float3)(lights[7], lights[8], lights[9]), mask)); + write_imagef(image, pixel, white_light(mix(fog_color, (float4)(0.40, 0.00, 0.40, 0.2), 1.0 - max(dist / 700.0f, (float)0)), (float3)(lights[7], lights[8], lights[9]), face_mask)); //write_imagef(image, pixel, (float4)(.73, .81, .89, 1.0)); return; } \ No newline at end of file diff --git a/src/Camera.cpp b/src/Camera.cpp index 01a9d4b..f90a1c5 100644 --- a/src/Camera.cpp +++ b/src/Camera.cpp @@ -7,10 +7,10 @@ Camera::Camera() { } -Camera::Camera(sf::Vector3f position, sf::Vector2f direction) : - position(position), direction(direction) +Camera::Camera(sf::Vector3f position, sf::Vector2f direction, sf::RenderWindow* window) : + position(position), direction(direction), window(window) { - + fixed = sf::Vector2i(sf::Vector2i(window->getSize().x/2, window->getSize().y/2)); } Camera::~Camera() { @@ -124,6 +124,36 @@ void Camera::recieve_event(VrEventPublisher* publisher, std::unique_ptrtype == vr::Event::KeyHeld) { + + vr::KeyPressed *key_event = static_cast(event.get()); + + if (key_event->code == sf::Keyboard::M) { + if (mouse_enabled) + mouse_enabled = false; + else + mouse_enabled = true; + } + } + + else if (event->type == vr::Event::MouseMoved) { + + if (mouse_enabled) { + + vr::MouseMoved *mouse_event = static_cast(event.get()); + + //deltas = fixed - sf::Mouse::getPosition(); + deltas = fixed - sf::Vector2i(mouse_event->x, mouse_event->y); + if (deltas != sf::Vector2i(0, 0) && mouse_enabled == true) { + + sf::Mouse::setPosition(fixed, *window); + slew_camera(sf::Vector2f( + deltas.y / 1200.0f, + deltas.x / 1200.0f + )); + } + } + } } diff --git a/src/Hardware_Caster.cpp b/src/Hardware_Caster.cpp index 769e201..3aa7c7f 100644 --- a/src/Hardware_Caster.cpp +++ b/src/Hardware_Caster.cpp @@ -158,7 +158,7 @@ void Hardware_Caster::create_viewport(int width, int height, float v_fov, float } } - create_buffer("viewport_matrix", sizeof(float) * 4 * view_res.x * view_res.y, viewport_matrix); + create_buffer("viewport_matrix", sizeof(float) * 4 * view_res.x * view_res.y, viewport_matrix, CL_MEM_USE_HOST_PTR); // Create the image that opencl's rays write to viewport_image = new sf::Uint8[width * height * 4]; @@ -181,15 +181,13 @@ void Hardware_Caster::create_viewport(int width, int height, float v_fov, float } -void Hardware_Caster::assign_lights(std::vector lights) { +void Hardware_Caster::assign_lights(std::vector *lights) { - std::cout << sizeof(Light); + this->lights = lights; - this->lights = std::vector(lights); + light_count = static_cast(lights->size()); - light_count = static_cast(lights.size()); - - create_buffer("lights", sizeof(float) * 10 * light_count, this->lights.data(), CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR); + create_buffer("lights", sizeof(float) * 10 * light_count, this->lights->data(), CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR); create_buffer("light_count", sizeof(int), &light_count); @@ -199,6 +197,53 @@ void Hardware_Caster::draw(sf::RenderWindow* window) { window->draw(viewport_sprite); } +void Hardware_Caster::test_edit_viewport(int width, int height, float v_fov, float h_fov) +{ + sf::Vector2i view_res(width, height); + + double y_increment_radians = DegreesToRadians(v_fov / view_res.y); + double x_increment_radians = DegreesToRadians(h_fov / view_res.x); + + for (int y = -view_res.y / 2; y < view_res.y / 2; y++) { + for (int x = -view_res.x / 2; x < view_res.x / 2; x++) { + + // The base ray direction to slew from + sf::Vector3f ray(1, 0, 0); + + // Y axis, pitch + ray = sf::Vector3f( + static_cast(ray.z * sin(y_increment_radians * y) + ray.x * cos(y_increment_radians * y)), + static_cast(ray.y), + static_cast(ray.z * cos(y_increment_radians * y) - ray.x * sin(y_increment_radians * y)) + ); + + // Z axis, yaw + ray = sf::Vector3f( + static_cast(ray.x * cos(x_increment_radians * x) - ray.y * sin(x_increment_radians * x)), + static_cast(ray.x * sin(x_increment_radians * x) + ray.y * cos(x_increment_radians * x)), + static_cast(ray.z) + ); + + // correct for the base ray pointing to (1, 0, 0) as (0, 0). Should equal (1.57, 0) + ray = sf::Vector3f( + static_cast(ray.z * sin(-1.57) + ray.x * cos(-1.57)), + static_cast(ray.y), + static_cast(ray.z * cos(-1.57) - ray.x * sin(-1.57)) + ); + + int index = (x + view_res.x / 2) + view_res.x * (y + view_res.y / 2); + ray = Normalize(ray); + + viewport_matrix[index] = sf::Vector4f( + ray.x, + ray.y, + ray.z, + 0 + ); + } + } +} + int Hardware_Caster::acquire_platform_and_device() { // Get the number of platforms diff --git a/src/Input.cpp b/src/Input.cpp index b24347b..cace90a 100644 --- a/src/Input.cpp +++ b/src/Input.cpp @@ -40,6 +40,8 @@ void Input::handle_held_keys() { // When they are depressed, remove them for (auto&& event: event_queue) { + + // Key if (event->type == vr::Event::KeyPressed) { vr::KeyPressed *e = static_cast(event.get()); held_keys.push_back(e->code); @@ -48,6 +50,8 @@ void Input::handle_held_keys() { vr::KeyReleased *e = static_cast(event.get()); held_keys.erase(std::remove(held_keys.begin(), held_keys.end(), e->code), held_keys.end()); } + + // Mouse Button else if (event->type == vr::Event::MouseButtonPressed) { vr::MouseButtonPressed *e = static_cast(event.get()); held_mouse_buttons.push_back(e->button); diff --git a/src/Old_Map.cpp b/src/Old_Map.cpp index 436bc1b..8ce46f3 100644 --- a/src/Old_Map.cpp +++ b/src/Old_Map.cpp @@ -138,14 +138,14 @@ void Old_Map::generate_terrain() { } - //for (int x = 0; x < dimensions.x / 10; x++) { - // for (int y = 0; y < dimensions.y / 10; y++) { + for (int x = 0; x < dimensions.x; x++) { + for (int y = 0; y < dimensions.y; y++) { // for (int z = 0; z < dimensions.z; z++) { - // if (rand() % 1000 < 1) - // voxel_data[x + dimensions.x * (y + dimensions.z * z)] = rand() % 6; + //if (rand() % 1000 < 1) + voxel_data[x + dimensions.x * (y + dimensions.z * 1)] = 6; // } - // } - //} + } + } } diff --git a/src/Software_Caster.cpp b/src/Software_Caster.cpp index 1ec1c67..41d3a56 100644 --- a/src/Software_Caster.cpp +++ b/src/Software_Caster.cpp @@ -82,10 +82,10 @@ void Software_Caster::create_viewport(int width, int height, float v_fov, float } -void Software_Caster::assign_lights(std::vector lights) { - this->lights = std::vector(lights); +void Software_Caster::assign_lights(std::vector *lights) { + this->lights = lights; - int light_count = static_cast(lights.size()); + int light_count = static_cast(lights->size()); } void Software_Caster::assign_map(Old_Map * map) { @@ -330,7 +330,7 @@ sf::Color Software_Caster::global_light(sf::Color in, sf::Vector3i mask) { in.a = in.a + (int)acos( DotProduct( - Normalize(lights.at(0).direction_cartesian), + Normalize(lights->at(0).direction_cartesian), Normalize(mask_f) ) )/ 2; diff --git a/src/main.cpp b/src/main.cpp index 0f59683..655b46d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -36,8 +36,8 @@ #include "Input.h" #include "Pub_Sub.h" -const int WINDOW_X = 1920; -const int WINDOW_Y = 1080; +const int WINDOW_X = 1000; +const int WINDOW_Y = 1000; const int WORK_SIZE = WINDOW_X * WINDOW_Y; const int MAP_X = 512; @@ -70,51 +70,64 @@ int main() { //Map _map(sf::Vector3i(0, 0, 0)); //_map.generate_octree(); - glewInit(); sf::RenderWindow window(sf::VideoMode(WINDOW_X, WINDOW_Y), "SFML"); + window.setMouseCursorVisible(false); - GL_Testing t; + /*GL_Testing t; t.compile_shader("../shaders/passthrough.frag", GL_Testing::Shader_Type::FRAGMENT); t.compile_shader("../shaders/passthrough.vert", GL_Testing::Shader_Type::VERTEX); t.create_program(); - t.create_buffers(); - + t.create_buffers();*/ - RayCaster *rc = new Hardware_Caster(); + // Start up the raycaster + Hardware_Caster *raycaster = new Hardware_Caster(); - if (rc->init() != 1) { + if (raycaster->init() != 1) { abort(); } - // Set up the raycaster - std::cout << "map..."; - sf::Vector3i map_dim(MAP_X, MAP_Y, MAP_Z); - Old_Map* map = new Old_Map(map_dim); + // Create and generate the old 3d array style map + Old_Map* map = new Old_Map(sf::Vector3i(MAP_X, MAP_Y, MAP_Z)); map->generate_terrain(); - rc->assign_map(map); + // Send the data to the GPU + raycaster->assign_map(map); + // Create a new camera with (starting position, direction) Camera *camera = new Camera( sf::Vector3f(50, 50, 50), - sf::Vector2f(0.0f, 1.5707f) + sf::Vector2f(1.5f, 0.0f), + &window ); - rc->assign_camera(camera); - rc->create_viewport(WINDOW_X, WINDOW_Y, 50.0f, 80.0f); + // *link* the camera to the GPU + raycaster->assign_camera(camera); + + // Generate and send the viewport to the GPU. Also creates the viewport texture + raycaster->create_viewport(WINDOW_X, WINDOW_Y, 50.0f, 50.0f); + + float w = 60.0; + float h = 90.0; + + // Light for the currently non functional Bling Phong shader Light l; - l.direction_cartesian = sf::Vector3f(1.5f, 1.2f, 0.5f); + l.direction_cartesian = sf::Vector3f(+1.5f, -1.2f, -0.5f); l.position = sf::Vector3f(100.0f, 100.0f, 100.0f); l.rgbi = sf::Vector4f(0.3f, 0.4f, 0.3f, 1.0f); - rc->assign_lights(std::vector{l}); + std::vector light_vec; + light_vec.push_back(l); + // *links* the lights to the GPU + raycaster->assign_lights(&light_vec); + + // Checks to see if proper data was uploaded, then sets the kernel args + raycaster->validate(); - rc->validate(); - // Done setting up raycaster // ========== DEBUG ========== fps_counter fps; @@ -145,7 +158,9 @@ int main() { Input input_handler; - input_handler.subscribe(camera, vr::Event::EventType::KeyHeld); + + camera->subscribe_to_publisher(&input_handler, vr::Event::EventType::KeyHeld); + camera->subscribe_to_publisher(&input_handler, vr::Event::EventType::MouseMoved); WindowHandler win_hand(&window); win_hand.subscribe_to_publisher(&input_handler, vr::Event::EventType::Closed); @@ -157,46 +172,15 @@ int main() { input_handler.consume_sf_events(&window); input_handler.handle_held_keys(); input_handler.dispatch_events(); - // Poll for events from the user - sf::Event event; - while (window.pollEvent(event)) { - - if (event.type == sf::Event::KeyPressed) { - if (event.key.code == sf::Keyboard::M) { - if (mouse_enabled) - mouse_enabled = false; - else - mouse_enabled = true; - } if (event.key.code == sf::Keyboard::R) { - reset = true; - } if (event.key.code == sf::Keyboard::X) { - std::vector tvf = sfml_get_float_input(&window); - if (tvf.size() == 3){ - sf::Vector3f tv3(tvf.at(0), tvf.at(1), tvf.at(2)); - camera->set_position(tv3); - } - } - } - } - if (mouse_enabled) { - if (reset) { - reset = false; - sf::Mouse::setPosition(sf::Vector2i(2560/2, 1080/2)); - prev_pos = sf::Vector2i(2560 / 2, 1080 / 2); - } - - deltas = prev_pos - sf::Mouse::getPosition(); - if (deltas != sf::Vector2i(0, 0) && mouse_enabled == true) { - - // Mouse movement - sf::Mouse::setPosition(fixed); - prev_pos = sf::Mouse::getPosition(); - camera->slew_camera(sf::Vector2f( - deltas.y / 600.0f, - deltas.x / 600.0f - )); - } + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Equal)) { + raycaster->test_edit_viewport(WINDOW_X, WINDOW_Y, w += 5, h += 5); + } + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Dash)) { + raycaster->test_edit_viewport(WINDOW_X, WINDOW_Y, w -= 5, h -= 5); + } + if (sf::Keyboard::isKeyPressed(sf::Keyboard::L)) { + light_vec.at(0).position.y += 0.5; } // Time keeping @@ -218,8 +202,8 @@ int main() { window.clear(sf::Color::Black); // Run the raycast - rc->compute(); - rc->draw(&window); + raycaster->compute(); + raycaster->draw(&window); window.popGLStates();