void init() { const char* svgFile = "butterfly.svg"; SVGPath* plist; plist = svgParseFromFile(svgFile); SVGPath* cur = plist; while (cur && cur->hasFill) { vg::Path path = vg::createPath(cur->numCmd, cur->cmd, cur->numData, cur->data); mPaths.push_back(path); vg::Paint paint = vg::createSolidPaint(cur->fillColor); mPaints.push_back(paint); cur = cur->next; } svgDelete(plist); mAAEnabled = ui::checkBoxAdd(25.0f, 83.0f, 41.0f, 99.0f, FALSE); gfx::gpu_timer_init(&gpuTimer); fwk::setCaption("GPU accelerated SVG rendering"); }
Game::Game(const std::string &level, float near, float far, float fov) : camera(fov, resolution.x/(float)resolution.y, near, far) , music(nullptr) , current_mode(MODE_READY) { composition = new RenderTarget(resolution, GL_RGB8, RenderTarget::DEPTH_BUFFER | RenderTarget::DOUBLE_BUFFER); geometry = new RenderTarget(resolution, GL_RGB8, RenderTarget::DEPTH_BUFFER); printf("Loading level %s\n", level.c_str()); std::string base_dir = PATH_BASE "data/levels/" + level; Config config = Config::parse(base_dir + "/level.cfg"); //Read config: start_position = config["/player/start_position"]->as_float(); sky_color = config["/environment/sky_color"]->as_color(); static const glm::vec2 terrain_scale = config["/environment/terrain/scale"]->as_vec2(); static const float fog_intensity = config["/environment/fog_intensity"]->as_float(); camera_offset = config["/player/camera/offset"]->as_vec3(); look_at_offset = config["/player/camera/look_at_offset"]->as_float(); movement_speed = config["/player/speed/normal"]->as_float(); brake_movement_speed = config["/player/speed/brake"]->as_float(); spawn_area_start = config["/game/spawn_area/start"]->as_float(); spawn_area_end = config["/game/spawn_area/end"]->as_float(); spawn_area_size = spawn_area_end - spawn_area_start; spawn_distance = config["/game/spawn_area/distance"]->as_float(); despawn_distance = config["/game/spawn_area/despawn_distance"]->as_float(); difficulty_increase = config["/game/difficulty_increase"]->as_float(); static const Shader::fog_t fog = { glm::vec4(sky_color.to_vec3(), 1.f), fog_intensity }; Shader::upload_fog(fog); TextureArray * colors = TextureArray::from_filename( (base_dir +"/color0.png").c_str(), (base_dir + "/color1.png").c_str(), nullptr); TextureArray * normals = TextureArray::from_filename( (base_dir +"/normal0.png").c_str(), (base_dir + "/normal1.png").c_str(), nullptr); terrain = new Terrain(base_dir + "/map.png", terrain_scale.x, terrain_scale.y, colors, normals); Data * path_file = Data::open(base_dir + "/path.svg"); SVGPath * svg_path = svgParse((char*) path_file->data()); delete path_file; std::vector<glm::vec3> path_nodes; for(int i=0; i< svg_path->npts; ++i) { path_nodes.push_back(glm::vec3( svg_path->pts[i*2], 0, svg_path->pts[i*2 + 1] ) * terrain_scale.x); } svgDelete(svg_path); Path::optimize_vector(path_nodes); // Gets max elevation for a radius around a point to avoid the rails cliping into the ground. for(glm::vec3 &v : path_nodes) { v.y = glm::max(glm::max(glm::max(terrain->height_at(v.x+1.5f, v.z),terrain->height_at(v.x-1.5f, v.z)),terrain->height_at(v.x, v.z+1.5f)),terrain->height_at(v.x, v.z-1.5f)) + 0.2f; } path = new Path(path_nodes, false); rails = new Rails(path, 1.f); rail_texture = Texture2D::from_filename(PATH_BASE "data/textures/rails.png"); rail_material.texture = rail_texture; //Setup player: player.canon_offset = config["/player/canon_offset"]->as_vec3(); player.canon_length = config["/player/canon_length"]->as_float(); //Configure lights: lights.ambient_intensity() = config["/environment/light/ambient"]->as_vec3(); lights.num_lights() = 1; lights.lights[0]->intensity = config["/environment/light/sunlight"]->as_vec3(); lights.lights[0]->type = MovableLight::DIRECTIONAL_LIGHT; //Load enemies: EnemyTemplate::init(Config::parse(base_dir + "/enemies.cfg"), this); //Set up camera: update_camera(); //Create particle systems: wind_velocity = config["/environment/wind_velocity"]->as_vec3(); gravity = glm::vec4(config["/environment/gravity"]->as_vec3(), 1.f); particle_shader = Shader::create_shader("particles"); passthru = Shader::create_shader("passthru"); static const Config particle_config = Config::parse(base_dir + "/particles.cfg"); static const float canon_inner_radius = particle_config["/particles/spawn_radius"]->as_float(); particle_textures = TextureArray::from_filename( PATH_BASE "data/textures/smoke.png", PATH_BASE "data/textures/fog.png", PATH_BASE "data/textures/particle.png", PATH_BASE "data/textures/fire1.png", PATH_BASE "data/textures/fire2.png", PATH_BASE "data/textures/fire3.png", nullptr); static const int max_attack_particles = particle_config["/particles/max_attack_particles"]->as_int(); static const int max_smoke_particles = particle_config["/particles/max_smoke_particles"]->as_int(); static const int max_dust_particles = particle_config["/particles/max_dust_particles"]->as_int(); static const int max_explosion_particles = particle_config["/particles/max_explosion_particles"]->as_int(); attack_particles = new HittingParticles(max_attack_particles, particle_textures, EnemyTemplate::max_num_enemies, false); attack_particles->config.gravity = gravity; system_configs.push_back(&(attack_particles->config)); attack_particles->config.spawn_area = glm::vec4(0.f, 0.f, 0.f, canon_inner_radius); for(auto &p : particle_types) { p.config = attack_particles->config; //Get reasonable defaults system_configs.push_back(&(p.config)); } read_particle_config(particle_config["/particles/light"], particle_types[LIGHT_PARTICLES].config); particle_types[LIGHT_PARTICLES].count = particle_config["/particles/light/count"]->as_int(); particle_types[LIGHT_PARTICLES].spawn_speed = particle_config["/particles/light/spawn_speed"]->as_float(); particle_types[LIGHT_PARTICLES].damage = particle_config["/particles/light/damage"]->as_float(); read_particle_config(particle_config["/particles/medium"], particle_types[MEDIUM_PARTICLES].config); particle_types[MEDIUM_PARTICLES].count = particle_config["/particles/medium/count"]->as_int(); particle_types[MEDIUM_PARTICLES].spawn_speed = particle_config["/particles/medium/spawn_speed"]->as_float(); particle_types[MEDIUM_PARTICLES].damage = particle_config["/particles/medium/damage"]->as_float(); read_particle_config(particle_config["/particles/heavy"], particle_types[HEAVY_PARTICLES].config); particle_types[HEAVY_PARTICLES].count = particle_config["/particles/heavy/count"]->as_int(); particle_types[HEAVY_PARTICLES].spawn_speed = particle_config["/particles/heavy/spawn_speed"]->as_float(); particle_types[HEAVY_PARTICLES].damage = particle_config["/particles/heavy/damage"]->as_float(); //Smoke: smoke = new ParticleSystem(max_smoke_particles, particle_textures, false); smoke->config.gravity = gravity; system_configs.push_back(&(smoke->config)); smoke->config.spawn_area = glm::vec4(0.f, 0.f, 0.f, canon_inner_radius * 2.0); read_particle_config(particle_config["/particles/smoke"], smoke->config); smoke_count = particle_config["/particles/smoke/count"]->as_int(); smoke_spawn_speed = particle_config["/particles/smoke/spawn_speed"]->as_float(); //Dust: dust = new ParticleSystem(max_dust_particles, particle_textures, true); read_particle_config(particle_config["/particles/dust"], dust->config); dust->config.gravity = gravity; system_configs.push_back(&(dust->config)); dust->config.spawn_area = particle_config["/particles/dust/spawn_area"]->as_vec4(); dust->config.avg_spawn_velocity = glm::vec4(particle_config["/particles/dust/avg_spawn_velocity"]->as_vec3(), 0); dust->avg_spawn_rate = particle_config["/particles/dust/avg_spawn_rate"]->as_float(); dust->spawn_rate_var = particle_config["/particles/dust/spawn_rate_var"]->as_float(); dust_spawn_ahead = particle_config["/particles/dust/spawn_ahead"]->as_float(); half_dust_spawn_area = glm::vec3(dust->config.spawn_area.x, dust->config.spawn_area.y, dust->config.spawn_area.z) / 2.f; dust->config.spawn_position = glm::vec4(player.position() - half_dust_spawn_area, 1.f); dust->update_config(); dust->spawn(dust->avg_spawn_rate * 5.0); dust->config.spawn_position += glm::vec4(path->at(player.path_position() + dust_spawn_ahead / 2.f), 0.f); dust->spawn(dust->avg_spawn_rate * 5.0); //Explosions explosions = new ParticleSystem(max_explosion_particles, particle_textures, false); hit_explosion = explosions->config; system_configs.push_back(&(hit_explosion)); kill_explosion = explosions->config; system_configs.push_back(&(kill_explosion)); read_particle_config(particle_config["/particles/hit_explosion"], hit_explosion); read_particle_config(particle_config["/particles/kill_explosion"], kill_explosion); explosions->config.gravity = gravity; system_configs.push_back(&(explosions->config)); hit_explosion_count = particle_config["/particles/hit_explosion/count"]->as_int(); kill_explosion_count = particle_config["/particles/kill_explosion/count"]->as_int(); kill_explosion.spawn_area = particle_config["/particles/kill_explosion/spawn_area"]->as_vec4(); kill_explosion.avg_spawn_velocity = glm::vec4(particle_config["/particles/kill_explosion/avg_spawn_velocity"]->as_vec3(), 0); hit_explosion.spawn_area = particle_config["/particles/hit_explosion/spawn_area"]->as_vec4(); hit_explosion.avg_spawn_velocity = glm::vec4(particle_config["/particles/hit_explosion/avg_spawn_velocity"]->as_vec3(), 0); update_wind_velocity(); //Setup HUD hud_scale = glm::vec2(resolution.x / 800.f, resolution.y / 600.f); hud_static_elements_tex = Texture2D::from_filename(PATH_BASE "/data/textures/hudStatic.png"); fullscreen_quad = new Quad(); fullscreen_quad->set_scale(glm::vec3(resolution.x,resolution.y,0)); hud_lightpos = glm::vec2(594,490) * hud_scale; hud_mediumpos = glm::vec2(644,490) * hud_scale; hud_heavypos = glm::vec2(700,490) * hud_scale; hud_choice_tex = Texture2D::from_filename(PATH_BASE "/data/textures/weap_select.png"); hud_choice_quad = new Quad(); hud_choice_quad->set_scale(glm::vec3(97,92,0) * glm::vec3(hud_scale , 0)); hud_break_tex = Texture2D::from_filename(PATH_BASE "/data/textures/breaks_ready.png"); hud_break_quad = new Quad(); hud_break_quad->set_scale(glm::vec3(190, 25, 0) * glm::vec3(hud_scale, 0)); hud_break_quad->set_position(glm::vec3(glm::vec2(30, 530) * hud_scale, 0.f)); life_text.set_color(hud_font_color); life_text.set_scale(20.0 * hud_scale.x); life_text.set_position(glm::vec3(glm::vec2(26.f, 44.5f) * hud_scale, 0.f)); //Highscore stuff: highscore = new Highscore(base_dir + "/highscore", NUM_HIGHSCORE_ENTRIES); float hs_scale = 40.f; glm::vec2 higscore_base = glm::vec2(735.f, 85.f); int i=0; for(Text &t : highscore_entries) { t.set_position(glm::vec3( (higscore_base + glm::vec2(0.f, (i++)*hs_scale)) * hud_scale, 0.f)); t.set_scale(hs_scale); t.set_text(""); t.set_alignment(Text::RIGHT_ALIGNED); } //Textures game_over_texture = Texture2D::from_filename(PATH_BASE "/data/textures/gameover.png"); startscreen_texture = Texture2D::from_filename(PATH_BASE "/data/textures/start_screen.png"); }
int main(int argc, char *argv[]) { GLFWwindow* window; const GLFWvidmode* mode; int width,height,i,j; struct SVGPath* bg; struct SVGPath* fg; struct SVGPath* it; float bounds[4],view[4],cx,cy,w,offx,offy; float t = 0.0f, pt = 0.0f; TESSalloc ma; TESStesselator* tess = 0; const int nvp = 6; unsigned char* vflags = 0; int nvflags = 0; #ifdef USE_POOL struct MemPool pool; unsigned char mem[1024*1024]; #else int allocated = 0; #endif TESS_NOTUSED(argc); TESS_NOTUSED(argv); if (!glfwInit()) { printf("Failed to init GLFW."); return -1; } printf("loading...\n"); // Load assets bg = svgParseFromFile("../Bin/bg.svg"); if (!bg) return -1; fg = svgParseFromFile("../Bin/fg.svg"); if (!fg) return -1; printf("go...\n"); // Flip y for (it = bg; it != NULL; it = it->next) for (i = 0; i < it->npts; ++i) it->pts[i*2+1] = -it->pts[i*2+1]; for (it = fg; it != NULL; it = it->next) for (i = 0; i < it->npts; ++i) it->pts[i*2+1] = -it->pts[i*2+1]; // Find FG bounds and center. bounds[0] = bounds[2] = fg->pts[0]; bounds[1] = bounds[3] = fg->pts[1]; for (it = fg; it != NULL; it = it->next) { for (i = 0; i < it->npts; ++i) { const float x = it->pts[i*2]; const float y = it->pts[i*2+1]; if (x < bounds[0]) bounds[0] = x; if (y < bounds[1]) bounds[1] = y; if (x > bounds[2]) bounds[2] = x; if (y > bounds[3]) bounds[3] = y; } } cx = (bounds[0]+bounds[2])/2; cy = (bounds[1]+bounds[3])/2; for (it = fg; it != NULL; it = it->next) { for (i = 0; i < it->npts; ++i) { it->pts[i*2] -= cx; it->pts[i*2+1] -= cy; } } // Find BG bounds. bounds[0] = bounds[2] = bg->pts[0]; bounds[1] = bounds[3] = bg->pts[1]; for (it = bg; it != NULL; it = it->next) { for (i = 0; i < it->npts; ++i) { const float x = it->pts[i*2]; const float y = it->pts[i*2+1]; if (x < bounds[0]) bounds[0] = x; if (y < bounds[1]) bounds[1] = y; if (x > bounds[2]) bounds[2] = x; if (y > bounds[3]) bounds[3] = y; } } #ifdef USE_POOL pool.size = 0; pool.cap = sizeof(mem); pool.buf = mem; memset(&ma, 0, sizeof(ma)); ma.memalloc = poolAlloc; ma.memfree = poolFree; ma.userData = (void*)&pool; ma.extraVertices = 256; // realloc not provided, allow 256 extra vertices. #else memset(&ma, 0, sizeof(ma)); ma.memalloc = stdAlloc; ma.memfree = stdFree; ma.userData = (void*)&allocated; ma.extraVertices = 256; // realloc not provided, allow 256 extra vertices. tess = tessNewTess(&ma); if (!tess) return -1; // Offset the foreground shape to center of the bg. offx = (bounds[2]+bounds[0])/2; offy = (bounds[3]+bounds[1])/2; for (it = fg; it != NULL; it = it->next) { for (i = 0; i < it->npts; ++i) { it->pts[i*2] += offx; it->pts[i*2+1] += offy; } } // Add contours. for (it = bg; it != NULL; it = it->next) tessAddContour(tess, 2, it->pts, sizeof(float)*2, it->npts); for (it = fg; it != NULL; it = it->next) tessAddContour(tess, 2, it->pts, sizeof(float)*2, it->npts); if (!tessTesselate(tess, TESS_WINDING_POSITIVE, TESS_POLYGONS, nvp, 2, 0)) return -1; printf("Memory used: %.1f kB\n", allocated/1024.0f); #endif mode = glfwGetVideoMode(glfwGetPrimaryMonitor()); width = mode->width - 40; height = mode->height - 80; window = glfwCreateWindow(width, height, "Libtess2 Demo", NULL, NULL); if (!window) { glfwTerminate(); return -1; } glfwSetKeyCallback(window, key); glfwMakeContextCurrent(window); // Adjust bounds so that we get nice view of the bg. cx = (bounds[0]+bounds[2])/2; cy = (bounds[3]+bounds[1])/2; w = (bounds[2]-bounds[0])/2; view[0] = cx - w*1.2f; view[2] = cx + w*1.2f; view[1] = cy - w*1.2f*(float)height/(float)width; view[3] = cy + w*1.2f*(float)height/(float)width; glfwSetTime(0); while (!glfwWindowShouldClose(window)) { float ct = (float)glfwGetTime(); if (run) t += ct - pt; pt = ct; // Update and render glViewport(0, 0, width, height); glClearColor(0.3f, 0.3f, 0.32f, 1.0f); glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_TEXTURE_2D); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(view[0],view[2],view[1],view[3],-1,1); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glDisable(GL_DEPTH_TEST); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); #ifdef USE_POOL pool.size = 0; // reset pool tess = tessNewTess(&ma); if (tess) { offx = (view[2]+view[0])/2 + sinf(t) * (view[2]-view[0])/2; offy = (view[3]+view[1])/2 + cosf(t*3.13f) * (view[3]-view[1])/6; for (it = fg; it != NULL; it = it->next) { for (i = 0; i < it->npts; ++i) { it->pts[i*2] += offx; it->pts[i*2+1] += offy; } } for (it = bg; it != NULL; it = it->next) tessAddContour(tess, 2, it->pts, sizeof(float)*2, it->npts); for (it = fg; it != NULL; it = it->next) tessAddContour(tess, 2, it->pts, sizeof(float)*2, it->npts); for (it = fg; it != NULL; it = it->next) { for (i = 0; i < it->npts; ++i) { it->pts[i*2] -= offx; it->pts[i*2+1] -= offy; } } // First combine contours and then triangulate, this removes unnecessary inner vertices. if (tessTesselate(tess, TESS_WINDING_POSITIVE, TESS_BOUNDARY_CONTOURS, 0, 0, 0)) { const float* verts = tessGetVertices(tess); const int* vinds = tessGetVertexIndices(tess); const int nverts = tessGetVertexCount(tess); const int* elems = tessGetElements(tess); const int nelems = tessGetElementCount(tess); if (nverts > nvflags) { if (vflags) free(vflags); nvflags = nverts; vflags = (unsigned char*)malloc(sizeof(unsigned char)*nvflags); } if (vflags) { // Vertex indices describe the order the indices were added and can be used // to map the tesselator output to input. Vertices marked as TESS_UNDEF // are the ones that were created at the intersection of segments. // That is, if vflags is set it means that the vertex comes from intersegment. for (i = 0; i < nverts; ++i) vflags[i] = vinds[i] == TESS_UNDEF ? 1 : 0; } for (i = 0; i < nelems; ++i) { int b = elems[i*2]; int n = elems[i*2+1]; tessAddContour(tess, 2, &verts[b*2], sizeof(float)*2, n); } if (!tessTesselate(tess, TESS_WINDING_POSITIVE, TESS_POLYGONS, nvp, 2, 0)) tess = 0; } else tess = 0; } #endif // Draw tesselated pieces. if (tess) { const float* verts = tessGetVertices(tess); const int* vinds = tessGetVertexIndices(tess); const int* elems = tessGetElements(tess); const int nverts = tessGetVertexCount(tess); const int nelems = tessGetElementCount(tess); // Draw polygons. glColor4ub(255,255,255,128); for (i = 0; i < nelems; ++i) { const int* p = &elems[i*nvp]; glBegin(GL_TRIANGLE_FAN); for (j = 0; j < nvp && p[j] != TESS_UNDEF; ++j) glVertex2f(verts[p[j]*2], verts[p[j]*2+1]); glEnd(); } glColor4ub(0,0,0,16); for (i = 0; i < nelems; ++i) { const int* p = &elems[i*nvp]; glBegin(GL_LINE_LOOP); for (j = 0; j < nvp && p[j] != TESS_UNDEF; ++j) glVertex2f(verts[p[j]*2], verts[p[j]*2+1]); glEnd(); } glColor4ub(0,0,0,128); glPointSize(3.0f); glBegin(GL_POINTS); for (i = 0; i < nverts; ++i) { if (vflags && vflags[vinds[i]]) glColor4ub(255,0,0,192); else glColor4ub(0,0,0,128); glVertex2f(verts[i*2], verts[i*2+1]); } glEnd(); glPointSize(1.0f); } glEnable(GL_DEPTH_TEST); glfwSwapBuffers(window); glfwPollEvents(); } if (tess) tessDeleteTess(tess); if (vflags) free(vflags); svgDelete(bg); svgDelete(fg); glfwTerminate(); return 0; }