Skip to content

Instantly share code, notes, and snippets.

@roxlu
Created March 2, 2013 17:23
Show Gist options
  • Save roxlu/5072080 to your computer and use it in GitHub Desktop.
Save roxlu/5072080 to your computer and use it in GitHub Desktop.
Oil Painting a la Petros Vrellis Starry Night
#include <Simulation.h>
#include <iostream>
Simulation* sim_ptr;
// CALLBACKS
// ---------
void window_size_callback(GLFWwindow* window, int w, int h);
int window_close_callback(GLFWwindow* window);
void mouse_button_callback(GLFWwindow* window, int button, int action);
void cursor_callback(GLFWwindow* window, int x, int y);
void scroll_callback(GLFWwindow* window, double x, double y);
void key_callback(GLFWwindow* window, int key, int action);
void error_callback(int err, const char* msg);
void char_callback(GLFWwindow* window, int chr);
// APPLICATION ENTRY
// -----------------
int main() {
int width = 800;
int height = 600;
sim_ptr = NULL;
int c = 0;
GLFWmonitor** m = glfwGetMonitors(&c);
// init
glfwSetErrorCallback(error_callback);
if(!glfwInit()) {
printf("ERROR: cannot initialize GLFW.\n");
exit(EXIT_FAILURE);
}
// glfwWindowHint(GLFW_DEPTH_BITS, 16);
//GLFWwindow window = glfwCreateWindow(width, height, GLFW_WINDOWED, "Simulation", NULL);
GLFWwindow* window = glfwCreateWindow(width, height, "Simulation", NULL, NULL);
if(!window) {
printf("ERROR: cannot open window.\n");
exit(EXIT_FAILURE);
}
glfwSetWindowSizeCallback(window, window_size_callback);
glfwSetWindowCloseCallback(window, window_close_callback);
glfwSetMouseButtonCallback(window, mouse_button_callback);
glfwSetCursorPosCallback(window, cursor_callback);
glfwSetScrollCallback(window, scroll_callback);
glfwSetKeyCallback(window, key_callback);
glfwSetCharCallback(window, char_callback);
glfwMakeContextCurrent(window);
// glewExperimental = true;
GLenum err = glewInit();
if (GLEW_OK != err) {
fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
exit(EXIT_FAILURE);
}
Simulation sim;
sim_ptr = &sim;
sim.window = window;
sim.window_w = width;
sim.window_h = height;
sim.setup();
bool running = true;
while(running) {
glfwPollEvents();
sim.update();
sim.draw();
glfwSwapBuffers(window);
running = !(glfwGetKey(window, GLFW_KEY_ESC));
}
glfwTerminate();
return EXIT_SUCCESS;
};
// CALLBACK DEFS
// --------------
void window_size_callback(GLFWwindow* window, int w, int h) {
if(sim_ptr) {
sim_ptr->onWindowResize(w,h);
}
}
int window_close_callback(GLFWwindow* window) {
if(sim_ptr) {
sim_ptr->onWindowClose();
}
return GL_TRUE;
}
void mouse_button_callback(GLFWwindow* window, int button, int action) {
if(!sim_ptr) {
return;
}
if(action == GLFW_PRESS) {
sim_ptr->onMouseDown(sim_ptr->mouse_x, sim_ptr->mouse_y, button);
sim_ptr->pressed_mouse_button = button;
sim_ptr->is_mouse_down = true;
}
else {
sim_ptr->onMouseUp(sim_ptr->mouse_x, sim_ptr->mouse_y, button);
sim_ptr->pressed_mouse_button = -1;
sim_ptr->is_mouse_down = false;
}
}
void cursor_callback(GLFWwindow* window, int x, int y) {
if(!sim_ptr) {
return;
}
sim_ptr->mouse_x = x;
sim_ptr->mouse_y = y;
if(sim_ptr->is_mouse_down) {
sim_ptr->onMouseDrag(x, y, (sim_ptr->prev_mouse_x - x), (sim_ptr->prev_mouse_y - y), sim_ptr->pressed_mouse_button);
}
else {
sim_ptr->onMouseMove(x,y);
}
sim_ptr->prev_mouse_x = sim_ptr->mouse_x;
sim_ptr->prev_mouse_y = sim_ptr->mouse_y;
}
void scroll_callback(GLFWwindow* window, double x, double y) {
if(!sim_ptr) {
return;
}
}
void char_callback(GLFWwindow* window, int ch) {
if(!sim_ptr) {
return;
}
sim_ptr->onChar(ch);
}
void key_callback(GLFWwindow* window, int key, int action) {
if(!sim_ptr) {
return;
}
if(action == GLFW_PRESS) {
sim_ptr->onKeyDown(key);
}
else {
sim_ptr->onKeyUp(key);
}
}
void error_callback(int err, const char* msg) {
printf("ERROR: %s, %d\n", msg, err);
}
#include "Oil.h"
Oil::Oil()
:gray_pixels(0)
,color_pixels(0)
{
}
Oil::~Oil() {
}
void Oil::setup(int w, int h, int components) {
this->w = w;
this->h = h;
this->components = components;
color_pixels = new unsigned char[w * h * components];
gray_pixels = new unsigned char[w * h];
}
void Oil::setPixels(unsigned char* pix) {
memcpy(color_pixels, pix, sizeof(unsigned char) * w * h * components);
for(int i = 0; i < w; ++i) {
for(int j = 0; j < h; ++j) {
int dx = j * w + i;
int src_dx = j * w * components + i * components;
gray_pixels[dx] = pix[src_dx + 0] * 0.2126
+ pix[src_dx + 1] * 0.07152
+ pix[src_dx + 2] * 0.0722;
}
}
}
void Oil::createVectors() {
vectors.clear();
vectors.assign(w * h, Vec2());
int d = 1;
for(int i = d; i < w - d; ++i) {
for(int j = d; j < h - d; ++j) {
int dxj = j;
float left = gray_pixels[DX(i-d,dxj,w)];
float right = gray_pixels[DX(i+d,dxj,w)];
float top = gray_pixels[DX(i,dxj-d,w)];
float bottom = gray_pixels[DX(i,dxj+d,w)];
float dx = (right - left) / 2;
float dy = (bottom - top) / 2;
float a = atan2(-dy,dx);
Vec2 vec(cos(a), sin(a));
vectors[DX(i,h-j,w)] = vec;
}
}
}
void Oil::update() {
}
#ifndef ROXLU_OIL_H
#define ROXLU_OIL_H
#include <assert.h>
#include <roxlu/Roxlu.h>
class Oil {
public:
Oil();
~Oil();
void setup(int w, int h, int components);
void update();
void setPixels(unsigned char* pix);
void createVectors();
unsigned char* getColorPixels();
unsigned char* getGrayPixels();
Vec2 getVector(int i, int j);
Vec3 getColor(int i, int j);
public:
std::vector<Vec2> vectors;
int w;
int h;
int components;
unsigned char* color_pixels;
unsigned char* gray_pixels;
};
inline unsigned char* Oil::getColorPixels() {
return color_pixels;
}
inline unsigned char* Oil::getGrayPixels() {
return gray_pixels;
}
inline Vec2 Oil::getVector(int i, int j) {
Vec2 result;
if(i < 0 || i > w) {
return result;
}
if(j < 0 || j > h) {
return result;
}
return vectors[DX(i, h-j, w)];
}
inline Vec3 Oil::getColor(int i, int j) {
assert(i < w && i >= 0);
assert(j < h && j >= 0);
unsigned char* p = color_pixels + (j * w * components + i * components);
Vec3 col(*(p + 0) / 255.0f, *(p + 1) / 255.0f, *(p + 2) / 255.0);
return col;
}
#endif
#include "OilDrawer.h"
OilDrawer::OilDrawer(Oil& o, Particles& ps)
:oil(o)
,ps(ps)
,debug_draw(false)
{
}
OilDrawer::~OilDrawer() {
}
GLuint OilDrawer::createTexture(int w, int h, int c, unsigned char* pixels) {
GLuint tmp;
glGenTextures(1, &tmp);
glBindTexture(GL_TEXTURE_2D, tmp);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, (c == 4) ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE, pixels);
return tmp;
}
void OilDrawer::setup() {
col_tex = createTexture(oil.w, oil.h, oil.components, oil.getColorPixels());
Image img;
img.load(rx_to_data_path("brush0.png"));
tex0 = createTexture(img.getWidth(), img.getHeight(), img.getComponents(), img.getPixels());
img.load(rx_to_data_path("brush1.png"));
tex1 = createTexture(img.getWidth(), img.getHeight(), img.getComponents(), img.getPixels());
img.load(rx_to_data_path("brush2.png"));
tex2 = createTexture(img.getWidth(), img.getHeight(), img.getComponents(), img.getPixels());
img.load(rx_to_data_path("brush3.png"));
tex3 = createTexture(img.getWidth(), img.getHeight(), img.getComponents(), img.getPixels());
prog = rx_create_shader(O_VS, O_FS);
glBindAttribLocation(prog, 0, "a_pos");
glBindAttribLocation(prog, 1, "a_tex");
glLinkProgram(prog);
glUseProgram(prog);
u_pm = glGetUniformLocation(prog, "u_projection_matrix");
u_mm = glGetUniformLocation(prog, "u_model_matrix");
u_color = glGetUniformLocation(prog, "u_color");
u_tex = glGetUniformLocation(prog, "u_tex");
glUniform1i(u_tex, 0);
printf("pm: %d, mm: %d\n", u_pm, u_mm);
ortho.orthoTopLeft(800, 600, 0.0, -1.0);
glUniformMatrix4fv(u_pm, 1, GL_FALSE, ortho.getPtr());
float ww = 16.0f;
float hh = 4.0f;
GLfloat vertices[] = {
-ww, -hh, 0.0f, 0.0f,
ww, -hh, 0.0f, 1.0f,
ww, hh, 1.0f, 1.0f,
-ww, -hh, 0.0f, 0.0f,
ww, hh, 1.0f, 1.0f,
-ww, hh, 1.0f, 0.0f
};
/*
-ww, -hh, 0.0f, 0.0f,
ww, -hh, 1.0f, 0.0f,
ww, hh, 1.0f, 1.0f,
-ww, -hh, 0.0f, 0.0f,
ww, hh, 1.0f, 1.0f,
-ww, hh, 0.0f, 1.0f
*/
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glGenBuffers(1,&vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 4, (GLvoid*)0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 4, (GLvoid*)8);
}
void OilDrawer::update() {
}
void OilDrawer::draw() {
// DEBUG
if(debug_draw) {
dd.drawTexture(col_tex, 0,0, 800, 600);
// VECTORS
glLineWidth(1.0f);
dd.begin(GL_LINES);
Vec4 col(1.0, 0.0, 1.0, 1.0);
int step = 16;
float len = step * 0.8;
for(int i = 0; i < oil.w - step; i += step) {
for(int j = 0; j < oil.h - step; j += step) {
Vec2& v = oil.vectors[DX(i,j,oil.w)];
Vec2 pos(i, j);
dd.addVertex(pos, col);
dd.addVertex(pos + (v * len), col);
}
}
dd.end();
#if 0
// PARTICLES
glPointSize(3.0f);
col.set(0.1, 0.8, 0.2, 1.0);
dd.begin(GL_POINTS);
for(int i = 0; i < ps.particles.size(); ++i) {
Particle& p = *ps.particles[i];
col.set(p.color.x, p.color.y, p.color.z, 1.0);
dd.addVertex(ps.particles[i]->position, col);
}
dd.end();
#endif
Mat4 ident;
dd.draw(ortho.getPtr(), ident.getPtr());
}
else {
// SHADED
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//glBlendFunc(GL_ONE, GL_SRC_COLOR);
//glBlendFunc(GL_ONE, GL_ONE);
glActiveTexture(GL_TEXTURE0);
glBindVertexArray(vao);
glUseProgram(prog);
Mat4 mm;
for(int i = 0; i < ps.particles.size(); ++i) {
Particle& p = *ps.particles[i];
mm.identity();
mm.setPosition(p.position.x, p.position.y, 0);
Vec3 v = p.velnorm;
mm[0] = v.x;
mm[1] = v.y;
mm[4] = -v.y;
mm[5] = v.x;
glUniformMatrix4fv(u_mm, 1, GL_FALSE, mm.getPtr());
glUniform3fv(u_color, 1, p.color.getPtr());
if(p.tex == 0) {
glBindTexture(GL_TEXTURE_2D, tex0);
}
else if(p.tex == 1) {
glBindTexture(GL_TEXTURE_2D, tex1);
}
else if(p.tex == 2) {
glBindTexture(GL_TEXTURE_2D, tex2);
}
else if(p.tex == 3) {
glBindTexture(GL_TEXTURE_2D, tex3);
}
glDrawArrays(GL_TRIANGLES, 0, 6);
}
}
}
#ifndef ROXLU_OIL_DRAWER_H
#define ROXLU_OIL_DRAWER_H
#include <roxlu/Roxlu.h>
#include "Oil.h"
#include "Particles.h"
static const char* O_VS = GLSL(120,
uniform mat4 u_projection_matrix;
uniform mat4 u_model_matrix;
attribute vec4 a_pos;
attribute vec2 a_tex;
varying vec2 v_tex;
void main() {
v_tex = a_tex;
gl_Position = u_projection_matrix * u_model_matrix * a_pos;
}
);
static const char* O_FS = GLSL(120,
varying vec2 v_tex;
uniform vec3 u_color;
uniform sampler2D u_tex;
void main() {
vec4 texcol = texture2D(u_tex, v_tex);
gl_FragColor.a = texcol.a * .5;
gl_FragColor.rgb = texcol.rgb * u_color;
}
);
class OilDrawer {
public:
OilDrawer(Oil& o, Particles& ps);
~OilDrawer();
void setup();
void update();
void draw();
GLuint createTexture(int w, int h, int c, unsigned char* pixels);
void toggleDebugDraw();
public:
bool debug_draw;
Oil& oil;
Particles& ps;
DebugDrawer dd;
GLuint gray_tex;
GLuint col_tex;
Mat4 ortho;
GLuint vbo;
GLuint vao;
GLuint prog;
GLuint tex0;
GLuint tex1;
GLuint tex2;
GLuint tex3;
GLint u_pm;
GLint u_mm;
GLint u_color;
GLint u_tex;
};
inline void OilDrawer::toggleDebugDraw() {
debug_draw = !debug_draw;
}
#endif
#include "Particles.h"
Particle::Particle(Vec3 pos, float m)
:position(pos)
{
if(m < 0.001) {
mass = 0.0f;
inv_mass = 0.0f;
}
else {
mass = m;
inv_mass = 1.0f / m;
}
tex = rx_random(0,4);
}
Particle::~Particle() {
}
void Particle::addForce(Vec3 f) {
forces += f;
}
void Particle::update(float dt) {
forces *= inv_mass;
velocity += (forces * dt);
position += (velocity * dt);
forces.set(0.0f, 0.0f, 0.0f);
velocity *= 0.99;
}
// ---
Particles::Particles(Oil& oil)
:oil(oil)
{
}
Particles::~Particles() {
for(int i = 0; i < particles.size(); ++i) {
delete particles[i];
}
particles.clear();
}
void Particles::addParticle(Particle* p) {
p->color = oil.getColor(p->position.x, p->position.y);
particles.push_back(p);
}
void Particles::update(float dt) {
for(int k = 0; k < 4; ++k) {
for(int i = 0; i < particles.size(); ++i) {
Particle& p = *particles[i];
Vec2 vec = oil.getVector(p.position.x, p.position.y);
Vec3 force = Vec3(vec.x, vec.y, 0);
p.addForce(force * 10);
particles[i]->update(dt);
}
}
for(int i = 0; i < particles.size(); ++i) {
Particle& p = *particles[i];
p.velnorm = p.velocity.getNormalized();
}
}
void Particles::addForce(Vec3 f) {
for(int i = 0; i < particles.size(); ++i) {
particles[i]->addForce(f);
}
}
#ifndef OIL_PARTICLES_H
#define OIL_PARTICLES_H
#include <vector>
#include <roxlu/Roxlu.h>
#include "Oil.h"
class Particle {
public:
Particle(Vec3 pos, float mass);
~Particle();
void addForce(Vec3 f);
void update(float dt);
public:
Vec3 color;
Vec3 position;
Vec3 forces;
Vec3 velocity;
Vec3 velnorm;
float mass;
float inv_mass;
int tex;
};
class Particles {
public:
Particles(Oil& oil);
~Particles();
void addParticle(Particle* p);
void addForce(Vec3 f);
void update(float dt);
public:
Oil& oil;
vector<Particle*> particles;
};
#endif
#include <Simulation.h>
Simulation::Simulation()
:SimulationBase()
,ps(oil)
,oil_drawer(oil, ps)
{
}
void Simulation::setup() {
Image img;
img.load(rx_to_data_path("asteroids.jpg"));
printf("%d, %d, %d\n", img.getWidth(), img.getHeight(), img.getComponents());
oil.setup(img.getWidth(), img.getHeight(), img.getComponents());
oil.setPixels(img.getPixels());
oil.createVectors();
oil_drawer.setup();
glClearColor(0.0, 0.0, 0.0, 0.0);
}
void Simulation::update() {
oil.update();
ps.update(1.0f/60.0f);
oil_drawer.update();
}
void Simulation::draw() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
oil_drawer.draw();
if(oil_drawer.debug_draw) {
fps.draw();
}
}
void Simulation::onMouseUp(int x, int y, int button) {
}
void Simulation::onMouseDown(int x, int y, int button) {
int d = 100;
int niter = 250;
for(int i = 0; i < niter; ++i) {
ps.addParticle(new Particle(Vec3(rx_random(x-d,x+d),rx_random(y-d, y+d),0), rx_random(1.0f, 5.0f)));
}
}
void Simulation::onMouseDrag(int x, int y, int dx, int dy, int button) {
}
void Simulation::onMouseMove(int x, int y) {
}
void Simulation::onChar(int ch) {
if(ch == 'f') {
int niter = 2500;
for(int i = 0; i < niter; ++i) {
ps.addParticle(new Particle(Vec3(rx_random(0, oil.w),rx_random(0, oil.h),0), rx_random(1.0f, 5.0f)));
}
}
else if(ch == 'd') {
oil_drawer.toggleDebugDraw();
}
}
void Simulation::onKeyDown(int key) {
}
void Simulation::onKeyUp(int key) {
}
void Simulation::onWindowClose() {
}
#include <glfw_wrapper/SimulationBase.h>
#include "Oil.h"
#include "OilDrawer.h"
#include "Particles.h"
class Simulation : public SimulationBase {
public:
Simulation();
void setup();
void update();
void draw();
void onMouseDown(int x, int y, int buton);
void onMouseUp(int x, int y, int button);
void onMouseDrag(int x, int y, int dx, int dy, int button);
void onMouseMove(int x, int y);
void onChar(int ch);
void onKeyDown(int key);
void onKeyUp(int key);
void onWindowClose();
private:
Oil oil;
Particles ps;
OilDrawer oil_drawer;
FPS fps;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment