mirror of
https://github.com/retroplasma/earth-reverse-engineering.git
synced 2026-01-18 14:06:47 +00:00
700 lines
21 KiB
C++
700 lines
21 KiB
C++
#include <fstream>
|
|
#include <SDL.h>
|
|
#if defined(_WIN32) || defined(__linux__)
|
|
#include <glad/glad.h>
|
|
#include "gl2/src/glad.c"
|
|
#else
|
|
#define GL_GLEXT_PROTOTYPES
|
|
#endif
|
|
#include <SDL_opengl.h>
|
|
|
|
#define HTTP_IMPLEMENTATION
|
|
#include "http.h"
|
|
#define STB_IMAGE_IMPLEMENTATION
|
|
#include "stb_image.h"
|
|
|
|
#include "imgui.h"
|
|
#include "examples/imgui_impl_sdl.h"
|
|
#include "examples/imgui_impl_opengl2.h"
|
|
#include "imgui.cpp"
|
|
#include "imgui_widgets.cpp"
|
|
#include "imgui_draw.cpp"
|
|
#include "examples/imgui_impl_sdl.cpp"
|
|
#include "examples/imgui_impl_opengl2.cpp"
|
|
|
|
#include "proto/rocktree.pb.h"
|
|
#include "proto/rocktree.pb.cc"
|
|
using namespace geo_globetrotter_proto_rocktree;
|
|
|
|
#include "vector_math.h"
|
|
|
|
SDL_Window* sdl_window;
|
|
|
|
const int MAX_LEVEL = 20;
|
|
|
|
typedef struct {
|
|
double n, s, w, e;
|
|
} llbounds_t;
|
|
|
|
void latLonToOctant(double lat, double lon, char octant[MAX_LEVEL]) {
|
|
octant[0] = 0;
|
|
octant[1] = 0;
|
|
llbounds_t box;
|
|
|
|
if (lat < 0.0) { octant[1] |= 2; box.n = 0.0; box.s = -90.0;}
|
|
else { octant[0] |= 2; box.n = 90.0; box.s = 0.0; }
|
|
|
|
if (lon < -90.0) { box.w = -180.0; box.e = -90.0; }
|
|
else if (lon < 0.0) { octant[1] |= 1; box.w = -90.0; box.e = 0.0; }
|
|
else if (lon < 90.0) { octant[0] |= 1; box.w = 0.0; box.e = 90.0; }
|
|
else { octant[0] |= 1; octant[1] |= 1; box.w = 90.0; box.e = 180.0; }
|
|
|
|
int level = MAX_LEVEL;
|
|
for (int i = 2; i < level; i++) {
|
|
octant[i] = 0;
|
|
|
|
double mid_lat = (box.n + box.s) / 2.0;
|
|
double mid_lon = (box.w + box.e) / 2.0;
|
|
|
|
if (lat < mid_lat) {
|
|
box.n = mid_lat;
|
|
} else {
|
|
box.s = mid_lat;
|
|
octant[i] |= 2;
|
|
}
|
|
|
|
if (lon < mid_lon) {
|
|
box.e = mid_lon;
|
|
} else {
|
|
box.w = mid_lon;
|
|
octant[i] |= 1;
|
|
}
|
|
}
|
|
|
|
// to ascii
|
|
for (int i = 0; i < level; i++) octant[i] += '0';
|
|
}
|
|
|
|
void getPathAndFlags(int path_id, char path[], int* level, int* flags) {
|
|
*level = 1 + (path_id & 3);
|
|
path_id >>= 2;
|
|
for (int i = 0; i < *level; i++) {
|
|
path[i] = '0' + (path_id & 7);
|
|
path_id >>= 3;
|
|
}
|
|
*flags = path_id;
|
|
}
|
|
|
|
bool fetchData(const char* path, unsigned char** data, size_t* len) {
|
|
const char* base_url = "http://kh.google.com/rt/earth/";
|
|
char* url = (char*)malloc(strlen(base_url) + strlen(path) + 1);
|
|
strcpy(url, base_url); strcat(url, path);
|
|
printf("GET %s\n", url);
|
|
http_t* request = http_get(url, NULL);
|
|
free(url);
|
|
if (!request) return false;
|
|
|
|
http_status_t status;
|
|
do {
|
|
status = http_process(request);
|
|
SDL_Delay(1);
|
|
} while (status == HTTP_STATUS_PENDING);
|
|
|
|
if (status == HTTP_STATUS_FAILED) {
|
|
http_release(request);
|
|
return false;
|
|
}
|
|
|
|
*data = (unsigned char*)malloc(request->response_size);
|
|
*len = request->response_size;
|
|
memcpy(*data, request->response_data, *len);
|
|
|
|
http_release(request);
|
|
return true;
|
|
}
|
|
|
|
PlanetoidMetadata* getPlanetoid() {
|
|
unsigned char* data; size_t len;
|
|
if (fetchData("PlanetoidMetadata", &data, &len)) {
|
|
PlanetoidMetadata* planetoid = new PlanetoidMetadata();
|
|
if (planetoid->ParseFromArray(data, len)) {
|
|
free(data);
|
|
return planetoid;
|
|
} else {
|
|
free(data);
|
|
delete planetoid;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
BulkMetadata* getBulk(const char* path, int epoch) {
|
|
char url_buf[200];
|
|
sprintf(url_buf, "BulkMetadata/pb=!1m2!1s%s!2u%d", path, epoch);
|
|
unsigned char* data; size_t len;
|
|
if (fetchData(url_buf, &data, &len)) {
|
|
BulkMetadata* bulk = new BulkMetadata();
|
|
if (bulk->ParseFromArray(data, len)) {
|
|
free(data);
|
|
return bulk;
|
|
} else {
|
|
free(data);
|
|
delete bulk;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
NodeData* getNode(const char* path, int epoch, int texture_format, int imagery_epoch) {
|
|
char url_buf[200];
|
|
sprintf(url_buf, "NodeData/pb=!1m2!1s%s!2u%d!2e%d!3u%d!4b0", path, epoch, texture_format, imagery_epoch);
|
|
unsigned char* data; size_t len;
|
|
if (fetchData(url_buf, &data, &len)) {
|
|
NodeData* node = new NodeData();
|
|
if (node->ParseFromArray(data, len)) {
|
|
free(data);
|
|
return node;
|
|
} else {
|
|
free(data);
|
|
delete node;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int unpackInt(std::string packed, int* index) {
|
|
int c = 0, d = 1;
|
|
int e;
|
|
do {
|
|
e = (unsigned char)packed[(*index)++];
|
|
c += (e & 0x7F) * d;
|
|
d <<= 7;
|
|
} while (e & 0x80);
|
|
return c;
|
|
}
|
|
|
|
// from minified js
|
|
int unpackIndices(std::string packed, unsigned short** indices) {
|
|
int i = 0;
|
|
int e = unpackInt(packed, &i);
|
|
int g = 0; // numNonDegenerateTriangles
|
|
unsigned short* buffer = new unsigned short[e];
|
|
for (int k = 0, m, p = 0, v = 0, z = 0; z < e; z++) {
|
|
int B = unpackInt(packed, &i);
|
|
m = p;
|
|
p = v;
|
|
v = k - B;
|
|
buffer[z] = v;
|
|
m != p && p != v && m != v && g++;
|
|
B || k++;
|
|
}
|
|
|
|
// TODO: move to single loop
|
|
int indices_len = 3 * g;
|
|
*indices = new unsigned short[indices_len];
|
|
indices_len = 0;
|
|
for (int i = 0; i < e - 2; i++) {
|
|
int a = buffer[i + 0];
|
|
int b = buffer[i + 1];
|
|
int c = buffer[i + 2];
|
|
if (a == b || a == c || b == c) continue;
|
|
if (i & 1) { // reverse winding
|
|
(*indices)[indices_len++] = a;
|
|
(*indices)[indices_len++] = c;
|
|
(*indices)[indices_len++] = b;
|
|
} else {
|
|
(*indices)[indices_len++] = a;
|
|
(*indices)[indices_len++] = b;
|
|
(*indices)[indices_len++] = c;
|
|
}
|
|
}
|
|
|
|
delete [] buffer;
|
|
|
|
return indices_len;
|
|
}
|
|
|
|
// from minified js (only positions)
|
|
int unpackVertices(std::string packed, unsigned char** vertices) {
|
|
int i = 0;
|
|
int h = packed.size();
|
|
int k = h / 3;
|
|
*vertices = new unsigned char[8 * k];
|
|
for (int m = 0; m < 3; m++) { // m == stride
|
|
int p = (unsigned char)packed[i++];
|
|
(*vertices)[m] = p;
|
|
for (int v = 1; v < k; v++) {
|
|
p = (p + packed[i++]) & 0xFF;
|
|
(*vertices)[8 * v + m] = p;
|
|
}
|
|
}
|
|
return 8 * k;
|
|
}
|
|
|
|
// from minified js
|
|
void unpackTexCoords(std::string packed, unsigned char* vertices, int vertices_len) {
|
|
int h = vertices_len / 8;
|
|
int i = 0;
|
|
int k = (unsigned char)packed[i++];
|
|
k += (unsigned char)packed[i++] << 8; // 65535
|
|
int g = (unsigned char)packed[i++];
|
|
g += (unsigned char)packed[i++] << 8; // 65535
|
|
assert(k == (1 << 16) - 1);
|
|
assert(g == (1 << 16) - 1);
|
|
int m = 0, p = 0;
|
|
for (int B = 0; B < h; B++) {
|
|
m = (m + ((unsigned char)packed[i + 0 * h + B] +
|
|
((unsigned char)packed[i + 2 * h + B] << 8))) & k; // 18418, 18455
|
|
p = (p + ((unsigned char)packed[i + 1 * h + B] +
|
|
((unsigned char)packed[i + 3 * h + B] << 8))) & g; // 49234, 45007
|
|
int A = 8 * B + 4;
|
|
vertices[A + 0] = m & 0xFF;
|
|
vertices[A + 1] = m >> 8;
|
|
vertices[A + 2] = p & 0xFF;
|
|
vertices[A + 3] = p >> 8;
|
|
}
|
|
}
|
|
|
|
struct PlanetMesh {
|
|
mat4_t transform;
|
|
GLuint vertices_buffer;
|
|
GLuint indices_buffer;
|
|
int element_count;
|
|
GLuint texture;
|
|
float uv_offset[2];
|
|
float uv_scale[2];
|
|
} planet_meshes[64];
|
|
int planet_mesh_count = 0;
|
|
float planet_radius;
|
|
//vec3_t planet_min;
|
|
//vec3_t planet_max;
|
|
|
|
void imageHalve(unsigned char* pixels, int width, int height, int comp, unsigned char* out) {
|
|
assert(width > 1 && height > 1);
|
|
int half_width = width / 2;
|
|
int half_height = height / 2;
|
|
for (int y = 0; y < half_height; y++) {
|
|
for (int x = 0; x < half_width; x++) {
|
|
for (int ci = 0; ci < comp; ci++) {
|
|
int c = (int)(pixels[comp * (width * (2 * y + 0) + (2 * x + 0)) + ci]
|
|
+ pixels[comp * (width * (2 * y + 0) + (2 * x + 1)) + ci]
|
|
+ pixels[comp * (width * (2 * y + 1) + (2 * x + 0)) + ci]
|
|
+ pixels[comp * (width * (2 * y + 1) + (2 * x + 1)) + ci]);
|
|
out[comp * (half_width * y + x) + ci] = (unsigned char)(c / 4);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void imageHalveHorizontally(unsigned char* pixels, int width, int height, int comp, unsigned char* out) {
|
|
assert(width > 1);
|
|
int half_width = width / 2;
|
|
for (int y = 0; y < height; y++) {
|
|
for (int x = 0; x < half_width; x++) {
|
|
for (int ci = 0; ci < comp; ci++) {
|
|
int c = (int)(pixels[comp * (width * y + 2 * x + 0) + ci]
|
|
+ pixels[comp * (width * y + 2 * x + 1) + ci]);
|
|
out[comp * (half_width * y + x) + ci] = (unsigned char)(c / 2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void imageHalveVertically(unsigned char* pixels, int width, int height, int comp, unsigned char* out) {
|
|
assert(height > 1);
|
|
int half_height = height / 2;
|
|
for (int y = 0; y < half_height; y++) {
|
|
for (int x = 0; x < width; x++) {
|
|
for (int ci = 0; ci < comp; ci++) {
|
|
int c = (int)(pixels[comp * (width * (2 * y + 0) + x) + ci]
|
|
+ pixels[comp * (width * (2 * y + 1) + x) + ci]);
|
|
out[comp * (width * y + x) + ci] = (unsigned char)(c / 2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void loadPlanet() {
|
|
PlanetoidMetadata* planetoid = getPlanetoid();
|
|
printf("earth radius: %f\n", planetoid->radius());
|
|
planet_radius = planetoid->radius();
|
|
|
|
int root_epoch = planetoid->root_node_metadata().epoch();
|
|
BulkMetadata* root_bulk = getBulk("", root_epoch);
|
|
|
|
for (auto node_meta : root_bulk->node_metadata()) {
|
|
char path[MAX_LEVEL+1];
|
|
int level, flags;
|
|
getPathAndFlags(node_meta.path_and_flags(), path, &level, &flags);
|
|
path[level] = '\0';
|
|
printf("path %.*s flag %d\n", level, path, flags);
|
|
|
|
if (!(flags & NodeMetadata_Flags_NODATA)) {
|
|
int imagery_epoch = node_meta.imagery_epoch();
|
|
if (flags & NodeMetadata_Flags_USE_IMAGERY_EPOCH) {
|
|
imagery_epoch = root_bulk->default_imagery_epoch();
|
|
}
|
|
NodeData* node = getNode(path, root_epoch, Texture_Format_DXT1, imagery_epoch);
|
|
if (node) {
|
|
PlanetMesh* planet_mesh = &planet_meshes[planet_mesh_count++];
|
|
for (int i = 0; i < 16; i++) {
|
|
planet_mesh->transform[i] = (float)node->matrix_globe_from_mesh(i);
|
|
}
|
|
for (auto mesh : node->meshes()) {
|
|
unsigned short* indices;
|
|
unsigned char* vertices;
|
|
int indices_len = unpackIndices(mesh.indices(), &indices);
|
|
int vertices_len = unpackVertices(mesh.vertices(), &vertices);
|
|
unpackTexCoords(mesh.texture_coordinates(), vertices, vertices_len);
|
|
|
|
planet_mesh->uv_offset[0] = mesh.uv_offset_and_scale(0);
|
|
planet_mesh->uv_offset[1] = mesh.uv_offset_and_scale(1);
|
|
planet_mesh->uv_scale[0] = mesh.uv_offset_and_scale(2);
|
|
planet_mesh->uv_scale[1] = mesh.uv_offset_and_scale(3);
|
|
|
|
glGenBuffers(1, &planet_mesh->vertices_buffer);
|
|
glBindBuffer(GL_ARRAY_BUFFER, planet_mesh->vertices_buffer);
|
|
glBufferData(GL_ARRAY_BUFFER, vertices_len * sizeof(unsigned char), vertices, GL_STATIC_DRAW);
|
|
glGenBuffers(1, &planet_mesh->indices_buffer);
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, planet_mesh->indices_buffer);
|
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices_len * sizeof(unsigned short), indices, GL_STATIC_DRAW);
|
|
planet_mesh->element_count = indices_len;
|
|
|
|
delete [] indices;
|
|
delete [] vertices;
|
|
|
|
glGenTextures(1, &planet_mesh->texture);
|
|
glBindTexture(GL_TEXTURE_2D, planet_mesh->texture);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
assert(mesh.texture().size() == 1);
|
|
auto tex = mesh.texture()[0];
|
|
if (tex.format() == Texture_Format_JPG) {
|
|
assert(tex.data().size() == 1); // else we have to concatenate
|
|
std::string data = tex.data()[0];
|
|
int width, height, comp;
|
|
unsigned char* pixels = stbi_load_from_memory((unsigned char*)&data[0],
|
|
data.size(), &width, &height, &comp, 0);
|
|
if (pixels != NULL) {
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels);
|
|
unsigned char* pixels2 = new unsigned char[width * height];
|
|
unsigned char* pixels_in = pixels;
|
|
unsigned char* pixels_out = pixels2;
|
|
for (int mipmap_level = 1; width > 1 && height > 1; mipmap_level++) {
|
|
if (width > 1 && height > 1) {
|
|
imageHalve(pixels_in, width, height, comp, pixels_out); width /= 2; height /= 2;
|
|
} else if (width > 1) {
|
|
imageHalveHorizontally(pixels_in, width, height, comp, pixels_out); width /= 2;
|
|
} else if (height > 1) {
|
|
imageHalveVertically(pixels_in, width, height, comp, pixels_out); height /= 2;
|
|
}
|
|
glTexImage2D(GL_TEXTURE_2D, mipmap_level, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels_out);
|
|
unsigned char* tmp = pixels_in; pixels_in = pixels_out; pixels_out = tmp; // swap
|
|
}
|
|
delete [] pixels2;
|
|
stbi_image_free(pixels);
|
|
}
|
|
} else assert(false); // TODO: other formats
|
|
}
|
|
delete node;
|
|
|
|
if (planet_mesh_count >= 8) return; // stop after a couple of nodes for now
|
|
}
|
|
}
|
|
|
|
// next level
|
|
if (level == 4 && !(flags & NodeMetadata_Flags_LEAF)) { // bulk
|
|
return; // don't
|
|
BulkMetadata* bulk = getBulk(path, root_epoch);
|
|
if (bulk != NULL) {
|
|
printf("metas %d\n", bulk->node_metadata().size());
|
|
for (auto meta2 : bulk->node_metadata()) {
|
|
getPathAndFlags(meta2.path_and_flags(), path, &level, &flags);
|
|
}
|
|
delete bulk;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
GLuint makeShader(const char* vert_src, const char* frag_src) {
|
|
GLuint vert_shader = glCreateShader(GL_VERTEX_SHADER);
|
|
glShaderSource(vert_shader, 1, &vert_src, NULL);
|
|
glCompileShader(vert_shader);
|
|
GLuint frag_shader = glCreateShader(GL_FRAGMENT_SHADER);
|
|
glShaderSource(frag_shader, 1, &frag_src, NULL);
|
|
glCompileShader(frag_shader);
|
|
GLuint program = glCreateProgram();
|
|
glAttachShader(program, vert_shader);
|
|
glAttachShader(program, frag_shader);
|
|
glLinkProgram(program);
|
|
glDetachShader(program, vert_shader);
|
|
glDetachShader(program, frag_shader);
|
|
glDeleteShader(vert_shader);
|
|
glDeleteShader(frag_shader);
|
|
return program;
|
|
}
|
|
|
|
GLuint program;
|
|
GLint transform_loc;
|
|
GLint uv_offset_loc;
|
|
GLint uv_scale_loc;
|
|
GLint texture_loc;
|
|
GLint position_loc;
|
|
GLint texcoords_loc;
|
|
void initGL() {
|
|
glEnable(GL_DEPTH_TEST);
|
|
glEnable(GL_CULL_FACE);
|
|
|
|
program = makeShader(
|
|
"uniform mat4 transform;"
|
|
"uniform vec2 uv_offset;"
|
|
"uniform vec2 uv_scale;"
|
|
"attribute vec3 position;"
|
|
"attribute vec2 texcoords;"
|
|
"varying vec2 v_texcoords;"
|
|
"void main() {"
|
|
" v_texcoords = (texcoords + uv_offset) * uv_scale;"
|
|
" gl_Position = transform * vec4(position, 1.0);"
|
|
"}",
|
|
|
|
"#ifdef GL_ES\n"
|
|
"precision mediump float;\n"
|
|
"#endif\n"
|
|
"uniform sampler2D texture;"
|
|
"varying vec2 v_texcoords;"
|
|
"void main() {"
|
|
" gl_FragColor = vec4(texture2D(texture, v_texcoords).rgb, 1.0);"
|
|
"}"
|
|
);
|
|
glUseProgram(program);
|
|
transform_loc = glGetUniformLocation(program, "transform");
|
|
uv_offset_loc = glGetUniformLocation(program, "uv_offset");
|
|
uv_scale_loc = glGetUniformLocation(program, "uv_scale");
|
|
texture_loc = glGetUniformLocation(program, "texture");
|
|
position_loc = glGetAttribLocation(program, "position");
|
|
texcoords_loc = glGetAttribLocation(program, "texcoords");
|
|
}
|
|
|
|
mat4_t cam_rot = {
|
|
0.0f, 0.0f, 1.0f, 0.0f,
|
|
1.0f, 0.0f, 0.0f, 0.0f,
|
|
0.0f, 1.0f, 0.0f, 0.0f,
|
|
0.0f, 0.0f, 0.0f, 1.0f,
|
|
};
|
|
//float cam_lat = 0.0f;
|
|
//float cam_lon = 0.0f;
|
|
int mouse_x = 0, mouse_y = 0;
|
|
int prev_mouse_x, prev_mouse_y;
|
|
void drawPlanet() {
|
|
mat4_t temp0, temp1, temp2, translation, rotation, scale, modelview, transform;
|
|
|
|
int width, height;
|
|
SDL_GL_GetDrawableSize(sdl_window, &width, &height);
|
|
glViewport(0, 0, width, height);
|
|
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
mat4_t projection;
|
|
float aspect_ratio = (float)width / (float)height;
|
|
float fov = 0.25f * (float)M_PI;
|
|
float right_plane, top_plane, near_plane = 0.1f;
|
|
if (aspect_ratio > 1.0f) {
|
|
right_plane = tanf(0.5f * fov) * near_plane;
|
|
top_plane = right_plane / aspect_ratio;
|
|
} else {
|
|
top_plane = tanf(0.5f * fov) * near_plane;
|
|
right_plane = aspect_ratio * top_plane;
|
|
}
|
|
MatrixFrustum(-right_plane, right_plane, -top_plane, top_plane, near_plane, 10.0f, projection);
|
|
|
|
prev_mouse_x = mouse_x; prev_mouse_y = mouse_y;
|
|
unsigned mouse_buttons = SDL_GetMouseState(&mouse_x, &mouse_y);
|
|
SDL_GetWindowSize(sdl_window, &width, &height);
|
|
if (mouse_buttons & SDL_BUTTON(SDL_BUTTON_LEFT) && (mouse_x != prev_mouse_x || mouse_y != prev_mouse_y)) {
|
|
vec3_t w0, w1;
|
|
w0[0] = 4.0f * (2.0f * prev_mouse_x / width - 1.0f) * right_plane / near_plane;
|
|
w0[1] = 4.0f * (1.0f - 2.0f * prev_mouse_y / height) * top_plane / near_plane;
|
|
float xxyy0 = 1.0f - (w0[0] * w0[0] + w0[1] * w0[1]);
|
|
w0[2] = sqrtf(xxyy0);
|
|
w1[0] = 4.0f * (2.0f * mouse_x / width - 1.0f) * right_plane / near_plane;
|
|
w1[1] = 4.0f * (1.0f - 2.0f * mouse_y / height) * top_plane / near_plane;
|
|
float xxyy1 = 1.0f - (w1[0] * w1[0] + w1[1] * w1[1]);
|
|
w1[2] = sqrtf(xxyy1);
|
|
if (xxyy0 >= 0.0f && xxyy1 >= 0.0f) {
|
|
vec3_t axis;
|
|
CrossProduct(w0, w1, axis);
|
|
VectorNormalize(axis);
|
|
float angle = acosf(DotProduct(w0, w1));
|
|
MatrixRotation(axis, angle, temp0);
|
|
MatrixCopy(cam_rot, temp1);
|
|
MatrixMultiply(temp0, temp1, cam_rot);
|
|
}
|
|
}
|
|
|
|
ImGui::Text("%.2f %.2f %.2f", cam_rot[0], cam_rot[4], cam_rot[8]);
|
|
ImGui::Text("%.2f %.2f %.2f", cam_rot[1], cam_rot[5], cam_rot[9]);
|
|
ImGui::Text("%.2f %.2f %.2f", cam_rot[2], cam_rot[6], cam_rot[10]);
|
|
|
|
float s = 1.0f / planet_radius;
|
|
vec3_t s3 = { s, s, s };
|
|
MatrixScale(s3, scale);
|
|
vec3_t t = { 0.0f, 0.0f, -4.0f };
|
|
MatrixTranslation(t, translation);
|
|
MatrixCopy(cam_rot, rotation);
|
|
MatrixMultiply(translation, rotation, temp1);
|
|
|
|
glUseProgram(program);
|
|
glEnableVertexAttribArray(position_loc);
|
|
glEnableVertexAttribArray(texcoords_loc);
|
|
for (int mesh_index = 0; mesh_index < planet_mesh_count; mesh_index++) {
|
|
PlanetMesh* planet_mesh = &planet_meshes[mesh_index];
|
|
|
|
MatrixMultiply(scale, planet_mesh->transform, temp0);
|
|
MatrixMultiply(temp1, temp0, modelview);
|
|
MatrixMultiply(projection, modelview, transform);
|
|
glUniformMatrix4fv(transform_loc, 1, GL_FALSE, transform);
|
|
glUniform2fv(uv_offset_loc, 1, planet_mesh->uv_offset);
|
|
glUniform2fv(uv_scale_loc, 1, planet_mesh->uv_scale);
|
|
|
|
glUniform1i(texture_loc, 0);
|
|
glBindTexture(GL_TEXTURE_2D, planet_mesh->texture);
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, planet_mesh->vertices_buffer);
|
|
glVertexAttribPointer(position_loc, 3, GL_UNSIGNED_BYTE, GL_FALSE, 8, (void*)0);
|
|
glVertexAttribPointer(texcoords_loc, 2, GL_UNSIGNED_SHORT, GL_FALSE, 8, (void*)4);
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, planet_mesh->indices_buffer);
|
|
glDrawElements(GL_TRIANGLES, planet_mesh->element_count, GL_UNSIGNED_SHORT, NULL);
|
|
}
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
glDisableVertexAttribArray(position_loc);
|
|
glDisableVertexAttribArray(texcoords_loc);
|
|
}
|
|
|
|
void init() {
|
|
#if 0
|
|
vec3_t x_axis = {1.0f, 0.0f, 0.0f};
|
|
vec3_t y_axis = {0.0f, 1.0f, 0.0f};
|
|
MatrixRotation(x_axis, (float)M_PI * cam_lat / 180.0f, x_rotation);
|
|
MatrixRotation(y_axis, (float)M_PI * cam_lon / 180.0f, y_rotation);
|
|
MatrixMultiply(x_rotation, y_rotation, temp0);
|
|
mat4_t base = {
|
|
0.0f, 0.0f, 1.0f, 0.0f,
|
|
1.0f, 0.0f, 0.0f, 0.0f,
|
|
0.0f, 1.0f, 0.0f, 0.0f,
|
|
0.0f, 0.0f, 0.0f, 1.0f,
|
|
};
|
|
MatrixMultiply(temp0, base, rotation);
|
|
#endif
|
|
}
|
|
|
|
bool quit = false;
|
|
void mainloop() {
|
|
SDL_Event sdl_event;
|
|
while (SDL_PollEvent(&sdl_event)) {
|
|
switch (sdl_event.type) {
|
|
case SDL_QUIT:
|
|
quit = true;
|
|
break;
|
|
case SDL_KEYDOWN:
|
|
if (sdl_event.key.keysym.sym == SDLK_ESCAPE) quit = true;
|
|
break;
|
|
#ifdef EMSCRIPTEN // Touch controls
|
|
case SDL_FINGERDOWN: {
|
|
float x = sdl_event.tfinger.x;
|
|
float y = sdl_event.tfinger.y;
|
|
} break;
|
|
#endif
|
|
}
|
|
ImGui_ImplSDL2_ProcessEvent(&sdl_event);
|
|
}
|
|
|
|
ImGui_ImplOpenGL2_NewFrame();
|
|
ImGui_ImplSDL2_NewFrame(sdl_window);
|
|
ImGui::NewFrame();
|
|
|
|
drawPlanet();
|
|
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
ImGui::Render();
|
|
glUseProgram(0);
|
|
ImGui_ImplOpenGL2_RenderDrawData(ImGui::GetDrawData());
|
|
|
|
SDL_GL_SwapWindow(sdl_window);
|
|
}
|
|
|
|
int main(int argc, char* argv[]) {
|
|
int video_width = 512;
|
|
int video_height = 512;
|
|
|
|
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
|
|
fprintf(stderr, "Couldn't init SDL2: %s\n", SDL_GetError());
|
|
exit(1);
|
|
}
|
|
#ifdef EMSCRIPTEN
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK,
|
|
SDL_GL_CONTEXT_PROFILE_ES);
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
|
|
#else
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK,
|
|
SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
|
|
#endif
|
|
//SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
|
|
//SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4);
|
|
sdl_window = SDL_CreateWindow("Earth Client",
|
|
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
|
video_width, video_height,
|
|
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
|
|
if (!sdl_window) {
|
|
fprintf(stderr, "Couldn't create window: %s\n", SDL_GetError());
|
|
exit(1);
|
|
}
|
|
SDL_GLContext gl_context = SDL_GL_CreateContext(sdl_window);
|
|
if (!gl_context) {
|
|
fprintf(stderr, "Couldn't create OpenGL context: %s\n", SDL_GetError());
|
|
exit(1);
|
|
}
|
|
SDL_GL_SetSwapInterval(1);
|
|
|
|
#if defined(_WIN32) || defined(__linux__)
|
|
// init glad
|
|
if (!gladLoadGL()) {
|
|
fprintf(stderr, "Failed to init glad\n");
|
|
exit(1);
|
|
}
|
|
#endif
|
|
|
|
IMGUI_CHECKVERSION();
|
|
ImGui::CreateContext();
|
|
ImGuiIO& io = ImGui::GetIO(); (void)io;
|
|
ImGui::StyleColorsLight();
|
|
|
|
ImGui_ImplSDL2_InitForOpenGL(sdl_window, gl_context);
|
|
ImGui_ImplOpenGL2_Init();
|
|
|
|
initGL();
|
|
loadPlanet();
|
|
init();
|
|
|
|
#ifdef EMSCRIPTEN
|
|
emscripten_set_main_loop(mainloop, 0, 1);
|
|
#else
|
|
while (!quit) mainloop();
|
|
#endif
|
|
|
|
ImGui_ImplOpenGL2_Shutdown();
|
|
ImGui_ImplSDL2_Shutdown();
|
|
ImGui::DestroyContext();
|
|
|
|
SDL_GL_DeleteContext(gl_context);
|
|
SDL_DestroyWindow(sdl_window);
|
|
SDL_Quit();
|
|
|
|
return 0;
|
|
} |