particles, sprite animation
This commit is contained in:
parent
5bdf311da9
commit
6aa44042d7
|
@ -79,19 +79,61 @@ component.sprite = Object.copy(component, {
|
|||
_enghook: make_sprite,
|
||||
});
|
||||
|
||||
Object.hide(component.sprite, 'rect');
|
||||
|
||||
component.sprite.impl = {
|
||||
toJSON() {
|
||||
var j = {};
|
||||
Object.keys(this).forEach(k => j[k] = this[k]);
|
||||
delete j.rect;
|
||||
return j;
|
||||
},
|
||||
|
||||
set path(x) {
|
||||
//cmd(12,this.id,prototypes.resani(this.gameobject.__proto__.toString(), x),this.rect);
|
||||
cmd(12,this.id,x,this.rect);
|
||||
if (this.cancel) {
|
||||
this.cancel();
|
||||
this.cancel = undefined;
|
||||
}
|
||||
|
||||
if (!Resources.is_animation(x)) {
|
||||
this.rect = component.sprite.rect;
|
||||
cmd(12,this.id,x,this.rect);
|
||||
}
|
||||
else {
|
||||
this.rect = SpriteAnim.make(x).frames[0].rect;
|
||||
cmd(12,this.id,x,this.rect);
|
||||
}
|
||||
},
|
||||
get path() {
|
||||
var s = cmd(116,this.id);
|
||||
if (s === "icons/no_tex.gif") return undefined;
|
||||
return s;
|
||||
//return prototypes.resavi(this.gameobject.__proto__.toString(), cmd(116,this.id));
|
||||
},
|
||||
|
||||
play() {
|
||||
var frame = 0;
|
||||
var anim = SpriteAnim.make(this.path);
|
||||
var advance = function() {
|
||||
frame = (frame+1)%anim.frames.length;
|
||||
this.rect = anim.frames[frame].rect;
|
||||
cmd(12,this.id,this.path,this.rect);
|
||||
this.cancel = this.gameobject.delay(advance.bind(this), anim.frames[frame].time);
|
||||
}
|
||||
advance.call(this);
|
||||
|
||||
},
|
||||
|
||||
stop() {
|
||||
if (!this.cancel) return;
|
||||
this.cancel();
|
||||
this.cancel = undefined;
|
||||
},
|
||||
setframe(f) {
|
||||
this.stop();
|
||||
var anim = SpriteAnim.make(this.path);
|
||||
if (!anim) return;
|
||||
this.rect = anim.frames[f].rect;
|
||||
cmd(12,this.id,this.path,this.rect);
|
||||
},
|
||||
|
||||
toString() { return "sprite"; },
|
||||
hide() { this.enabled = false; },
|
||||
show() { this.enabled = true; },
|
||||
|
@ -163,12 +205,18 @@ sprite.inputs.kp2 = function() { this.pos = this.dimensions().scale([-0.5,-1]);
|
|||
sprite.inputs.kp1 = function() { this.pos = this.dimensions().scale([-1,-1]); };
|
||||
Object.seal(sprite);
|
||||
|
||||
/* sprite anim returns a data structure for the given file path
|
||||
frames: array of frames
|
||||
rect: frame rectangle
|
||||
time: miliseconds to hold the frame for
|
||||
loop: true if it should be looped
|
||||
*/
|
||||
var SpriteAnim = {
|
||||
make(path) {
|
||||
if (path.ext() === 'gif')
|
||||
return SpriteAnim.gif(path);
|
||||
else if (Resources.is_image(path))
|
||||
return SpriteAnim.strip(path);
|
||||
else
|
||||
return undefined;
|
||||
},
|
||||
gif(path) {
|
||||
var anim = {};
|
||||
|
@ -187,11 +235,13 @@ var SpriteAnim = {
|
|||
frame.time = 0.05;
|
||||
anim.frames.push(frame);
|
||||
}
|
||||
var times = cmd(224,path);
|
||||
for (var i = 0; i < frames; i++)
|
||||
anim.frames[i].time = times[i]/1000;
|
||||
anim.loop = true;
|
||||
var dim = Resources.texture.dimensions(path);
|
||||
dim.y /= frames;
|
||||
anim.dim = dim;
|
||||
anim.toJSON = function() { return {}; };
|
||||
return anim;
|
||||
},
|
||||
|
||||
|
@ -268,106 +318,6 @@ SpriteAnim.strip.doc = 'Given a path and number of frames, converts a horizontal
|
|||
SpriteAnim.aseprite.doc = 'Given an aseprite json metadata, returns an object of animations defined in the aseprite file.';
|
||||
SpriteAnim.find.doc = 'Given a path, find the relevant animation for the file.';
|
||||
|
||||
/* Container to play sprites and anim2ds */
|
||||
|
||||
component.char2d = Object.create(component.sprite);
|
||||
component.char2dimpl = {
|
||||
boundingbox() {
|
||||
var dim = this.acur.dim.slice();
|
||||
dim = dim.scale(this.gameobject.scale);
|
||||
var realpos = this.pos.slice();
|
||||
realpos.x = realpos.x * dim.x + (dim.x/2);
|
||||
realpos.y = realpos.y * dim.y + (dim.y/2);
|
||||
return cwh2bb(realpos,dim);
|
||||
},
|
||||
|
||||
anims:{},
|
||||
acur:{},
|
||||
frame: 0,
|
||||
|
||||
play_anim(anim) {
|
||||
this.acur = anim;
|
||||
this.frame = 0;
|
||||
this.gameobject.delay(this.advance.bind(this), this.acur.frames[this.frame].time);
|
||||
this.setsprite();
|
||||
},
|
||||
|
||||
play(name) {
|
||||
if (!(name in this)) {
|
||||
Log.info("Can't find an animation named " + name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.acur === this[name]) {
|
||||
this.timer.start();
|
||||
return;
|
||||
}
|
||||
|
||||
this.acur = this[name];
|
||||
this.frame = 0;
|
||||
this.timer.time = this.acur.frames[this.frame].time;
|
||||
this.timer.start();
|
||||
this.setsprite();
|
||||
},
|
||||
|
||||
setsprite() {
|
||||
this.rect = this.acur.frames[this.frame].rect;
|
||||
this.path = this.path;
|
||||
},
|
||||
|
||||
advance() {
|
||||
this.frame = (this.frame + 1) % this.acur.frames.length;
|
||||
this.setsprite();
|
||||
this.gameobject.delay(this.advance.bind(this), this.acur.frames[this.frame].time);
|
||||
},
|
||||
|
||||
devance() {
|
||||
this.frame = (this.frame - 1);
|
||||
if (this.frame === -1) this.frame = this.acur.frames-1;
|
||||
this.setsprite();
|
||||
},
|
||||
|
||||
setframe(frame) {
|
||||
this.frame = frame;
|
||||
this.setsprite();
|
||||
},
|
||||
|
||||
pause() {
|
||||
this.timer.pause();
|
||||
},
|
||||
|
||||
stop() {
|
||||
this.setframe(0);
|
||||
this.timer.stop();
|
||||
},
|
||||
|
||||
kill() { cmd(9, this.id); },
|
||||
|
||||
add_anim(anim,name) {
|
||||
if (name in this) return;
|
||||
this[name] = function() {
|
||||
this.play_anim(anim);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
component.char2dimpl.doc = {
|
||||
doc: "An animation player for sprites.",
|
||||
frame: "The current frame of animation.",
|
||||
anims: "A list of all animations in this player.",
|
||||
acur: "The currently playing animation object.",
|
||||
advance: "Advance the animation by one frame.",
|
||||
devance: "Go back one frame in the animation.",
|
||||
setframe: "Set a specific frame of animation.",
|
||||
stop: "Stops the animation and returns to the first frame.",
|
||||
pause: "Pauses the animation sequence in place.",
|
||||
play: "Given an animation string, play it. Equivalent to anim.[name].play().",
|
||||
play_anim: "Play a given animation object.",
|
||||
add_anim: "Add an animation object with the given name."
|
||||
};
|
||||
|
||||
Object.hide(component.char2dimpl, "doc");
|
||||
|
||||
/* Returns points specifying this geometry, with ccw */
|
||||
var Geometry = {
|
||||
box(w, h) {
|
||||
|
@ -907,7 +857,8 @@ component.circle2d.impl = Object.mix({
|
|||
get pos() { return this.offset; },
|
||||
set pos(x) { this.offset = x; },
|
||||
|
||||
}, collider2d.impl);;
|
||||
}, collider2d.impl);
|
||||
|
||||
|
||||
/* ASSETS */
|
||||
|
||||
|
|
|
@ -82,6 +82,7 @@ var timer = {
|
|||
|
||||
kill() {
|
||||
Register.unregister_obj(this);
|
||||
this.fn = undefined;
|
||||
},
|
||||
|
||||
delay(fn, secs) {
|
||||
|
|
|
@ -1382,6 +1382,10 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
|
|||
ret = ptr2js(cpGearJointNew(js2gameobject(argv[1])->body, js2gameobject(argv[2])->body, js2number(argv[3]), js2number(argv[4])));
|
||||
cpSpaceAddConstraint(space,js2ptr(ret));
|
||||
break;
|
||||
case 224:
|
||||
str = js2str(argv[1]);
|
||||
ret = ints2js(gif_delays(str));
|
||||
break;
|
||||
}
|
||||
|
||||
if (str)
|
||||
|
|
|
@ -1,36 +1,184 @@
|
|||
#include "particle.h"
|
||||
#include "stb_ds.h"
|
||||
#include "freelist.h"
|
||||
#include "render.h"
|
||||
#include "particle.sglsl.h"
|
||||
#include "log.h"
|
||||
|
||||
struct emitter make_emitter() {
|
||||
struct emitter e = {0};
|
||||
arrsetcap(e.particles, 200);
|
||||
static emitter **emitters;
|
||||
|
||||
static sg_shader par_shader;
|
||||
static sg_pipeline par_pipe;
|
||||
static sg_bindings par_bind;
|
||||
static int draw_count;
|
||||
|
||||
struct par_vert {
|
||||
HMM_Vec2 pos;
|
||||
float angle;
|
||||
float scale;
|
||||
struct rgba color;
|
||||
};
|
||||
|
||||
typedef struct par_vert par_vert;
|
||||
|
||||
void particle_init()
|
||||
{
|
||||
par_shader = sg_make_shader(particle_shader_desc(sg_query_backend()));
|
||||
|
||||
par_pipe = sg_make_pipeline(&(sg_pipeline_desc){
|
||||
.shader = par_shader,
|
||||
.layout = {
|
||||
.attrs = {
|
||||
[1].format = SG_VERTEXFORMAT_FLOAT2,
|
||||
[2].format = SG_VERTEXFORMAT_FLOAT,
|
||||
[3].format = SG_VERTEXFORMAT_FLOAT,
|
||||
[4].format = SG_VERTEXFORMAT_UBYTE4N,
|
||||
[0].format = SG_VERTEXFORMAT_FLOAT2,
|
||||
[0].buffer_index = 1
|
||||
},
|
||||
.buffers[0].step_func = SG_VERTEXSTEP_PER_INSTANCE,
|
||||
},
|
||||
.primitive_type = SG_PRIMITIVETYPE_TRIANGLE_STRIP,
|
||||
.label = "particle pipeline",
|
||||
.cull_mode = SG_CULLMODE_BACK,
|
||||
.colors[0].blend = blend_trans,
|
||||
.depth = {
|
||||
.write_enabled = true,
|
||||
.compare = SG_COMPAREFUNC_LESS_EQUAL,
|
||||
.pixel_format = SG_PIXELFORMAT_DEPTH
|
||||
}
|
||||
});
|
||||
|
||||
par_bind.vertex_buffers[0] = sg_make_buffer(&(sg_buffer_desc){
|
||||
.size = sizeof(par_vert)*500,
|
||||
.type = SG_BUFFERTYPE_VERTEXBUFFER,
|
||||
.usage = SG_USAGE_STREAM,
|
||||
.label = "particle buffer"
|
||||
});
|
||||
|
||||
float circleverts[8] = {
|
||||
0,0,
|
||||
0,1,
|
||||
1,0,
|
||||
1,1,
|
||||
};
|
||||
|
||||
par_bind.vertex_buffers[1] = sg_make_buffer(&(sg_buffer_desc){
|
||||
.data = (sg_range){.ptr = circleverts, .size = sizeof(float)*8},
|
||||
.usage = SG_USAGE_IMMUTABLE
|
||||
});
|
||||
|
||||
par_bind.fs.samplers[0] = sg_make_sampler(&(sg_sampler_desc){});
|
||||
}
|
||||
|
||||
emitter *make_emitter() {
|
||||
emitter *e = NULL;
|
||||
e = malloc(sizeof(*e));
|
||||
e->max = 20;
|
||||
arrsetlen(e->particles, e->max);
|
||||
for (int i = 0; i < arrlen(e->particles); i++)
|
||||
e->particles[i].life = 0;
|
||||
|
||||
e->life = 10;
|
||||
e->explosiveness = 0;
|
||||
e->tte = e->life/e->max;
|
||||
e->color = color_white;
|
||||
e->scale = 20;
|
||||
arrpush(emitters,e);
|
||||
return e;
|
||||
}
|
||||
|
||||
void free_emitter(struct emitter e) {
|
||||
arrfree(e.particles);
|
||||
void free_emitter(emitter *e)
|
||||
{
|
||||
arrfree(e->particles);
|
||||
for (int i = arrlen(emitters)-1; i >= 0; i--)
|
||||
if (emitters[i] == e) {
|
||||
arrdelswap(emitters,i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void start_emitter(struct emitter e) {
|
||||
void start_emitter(emitter *e)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void pause_emitter(struct emitter e) {
|
||||
void pause_emitter(emitter *e) {
|
||||
|
||||
}
|
||||
|
||||
void stop_emitter(struct emitter e) {
|
||||
void stop_emitter(emitter *e) {
|
||||
|
||||
}
|
||||
|
||||
void emitter_step(struct emitter e, double dt) {
|
||||
for (int i = 0; i < arrlen(e.particles); i++) {
|
||||
e.particles[i].pos = HMM_AddV3(e.particles[i].pos, HMM_MulV3F(e.particles[i].v, dt));
|
||||
e.particles[i].angle = HMM_MulQ(e.particles[i].angle, HMM_MulQF(e.particles[i].angle, dt));
|
||||
e.particles[i].life -= dt;
|
||||
int emitter_spawn(emitter *e)
|
||||
{
|
||||
for (int i = 0; i < e->max; i++) {
|
||||
if (e->particles[i].life > 0) continue;
|
||||
e->particles[i].life = e->life;
|
||||
e->particles[i].pos = (HMM_Vec3){0,0,0};
|
||||
e->particles[i].v = (HMM_Vec3){20,1,0};
|
||||
e->particles[i].angle = 0;
|
||||
e->particles[i].av = 1;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (e.particles[i].life <= 0)
|
||||
arrdelswap(e.particles, i);
|
||||
void emitter_emit(emitter *e, int count)
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
if (!emitter_spawn(e)) return;
|
||||
}
|
||||
|
||||
void emitters_step(double dt)
|
||||
{
|
||||
for (int i = 0; i < arrlen(emitters); i++)
|
||||
emitter_step(emitters[i], dt);
|
||||
}
|
||||
|
||||
void emitters_draw()
|
||||
{
|
||||
|
||||
draw_count = 0;
|
||||
|
||||
for (int i = 0; i < arrlen(emitters); i++) {
|
||||
emitter *e = emitters[i];
|
||||
par_bind.fs.images[0] = e->texture->id;
|
||||
for (int j = 0; j < arrlen(e->particles); j++) {
|
||||
particle *p = &e->particles[j];
|
||||
if (p->life <= 0) continue;
|
||||
struct par_vert pv;
|
||||
pv.pos = p->pos.xy;
|
||||
pv.angle = p->angle;
|
||||
pv.scale = p->scale;
|
||||
pv.color = p->color;
|
||||
sg_append_buffer(par_bind.vertex_buffers[0], &(sg_range){.ptr=&pv, .size=sizeof(struct par_vert)});
|
||||
draw_count++;
|
||||
}
|
||||
}
|
||||
if (draw_count == 0) return;
|
||||
sg_apply_pipeline(par_pipe);
|
||||
sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, SG_RANGE_REF(projection));
|
||||
sg_apply_bindings(&par_bind);
|
||||
sg_draw(0, 4, draw_count);
|
||||
}
|
||||
|
||||
void emitter_step(emitter *e, double dt) {
|
||||
for (int i = 0; i < arrlen(e->particles); i++) {
|
||||
if (e->particles[i].life <= 0) continue;
|
||||
e->particles[i].pos = HMM_AddV3(e->particles[i].pos, HMM_MulV3F(e->particles[i].v, dt));
|
||||
e->particles[i].angle += e->particles[i].av*dt;
|
||||
e->particles[i].life -= dt;
|
||||
e->particles[i].color = e->color;
|
||||
e->particles[i].scale = e->scale;
|
||||
|
||||
if (e->particles[i].life <= 0)
|
||||
e->particles[i].life = 0;
|
||||
}
|
||||
|
||||
e->tte-=dt;
|
||||
if (e->tte <= 0) {
|
||||
emitter_emit(e,1);
|
||||
e->tte = e->life/e->max;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,34 +3,42 @@
|
|||
|
||||
#include "HandmadeMath.h"
|
||||
#include "transform.h"
|
||||
#include "texture.h"
|
||||
|
||||
struct particle {
|
||||
typedef struct particle {
|
||||
HMM_Vec3 pos;
|
||||
HMM_Vec3 v; /* velocity */
|
||||
HMM_Quat angle;
|
||||
HMM_Quat av; /* angular velocity */
|
||||
|
||||
union {
|
||||
double life;
|
||||
unsigned int next;
|
||||
};
|
||||
};
|
||||
float angle;
|
||||
float av; /* angular velocity */
|
||||
float scale;
|
||||
double life;
|
||||
rgba color;
|
||||
} particle;
|
||||
|
||||
typedef struct emitter {
|
||||
struct particle *particles;
|
||||
transform3d t;
|
||||
int max;
|
||||
double life;
|
||||
void (*seeder)(struct particle *p); /* Called to initialize each particle */
|
||||
float explosiveness; /* 0 for a stream, 1 for all at once. Range of values allowed. */
|
||||
int max; /* number of particles */
|
||||
double life; /* how long a particle lasts */
|
||||
double tte; /* time to emit */
|
||||
rgba color;
|
||||
float scale;
|
||||
texture *texture;
|
||||
} emitter;
|
||||
|
||||
struct emitter make_emitter();
|
||||
void free_emitter(struct emitter e);
|
||||
void particle_init();
|
||||
|
||||
void start_emitter(struct emitter e);
|
||||
void pause_emitter(struct emitter e);
|
||||
void stop_emitter(struct emitter e);
|
||||
emitter *make_emitter();
|
||||
void free_emitter(emitter *e);
|
||||
|
||||
void emitter_step(struct emitter e, double dt);
|
||||
void start_emitter(emitter *e);
|
||||
void pause_emitter(emitter *e);
|
||||
void stop_emitter(emitter *e);
|
||||
|
||||
void emitter_emit(emitter *e, int count);
|
||||
void emitters_step(double dt);
|
||||
void emitters_draw();
|
||||
void emitter_step(emitter *e, double dt);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "gameobject.h"
|
||||
#include "log.h"
|
||||
#include "sprite.h"
|
||||
#include "particle.h"
|
||||
#include "window.h"
|
||||
#include "model.h"
|
||||
#include "stb_ds.h"
|
||||
|
@ -512,6 +513,8 @@ void full_2d_pass(struct window *window)
|
|||
model_draw_all();
|
||||
call_draw();
|
||||
|
||||
emitters_draw();
|
||||
|
||||
//// DEBUG
|
||||
if (debugDrawPhysics) {
|
||||
gameobject_draw_debugs();
|
||||
|
|
|
@ -91,6 +91,8 @@ struct rgba {
|
|||
unsigned char a;
|
||||
};
|
||||
|
||||
typedef struct rgba rgba;
|
||||
|
||||
struct boundingbox {
|
||||
float t;
|
||||
float b;
|
||||
|
|
|
@ -277,7 +277,6 @@ void slice9_draw(const char *img, HMM_Vec2 pos, HMM_Vec2 dimensions, struct rgba
|
|||
|
||||
sg_draw(sprite_count * 4, 4, 1);
|
||||
sprite_count++;
|
||||
|
||||
}
|
||||
|
||||
void sprite_setframe(struct sprite *sprite, struct glrect *frame) {
|
||||
|
|
|
@ -76,6 +76,12 @@ int gif_nframes(const char *path)
|
|||
return t->frames;
|
||||
}
|
||||
|
||||
int *gif_delays(const char *path)
|
||||
{
|
||||
struct Texture *t = texture_pullfromfile(path);
|
||||
return t->delays;
|
||||
}
|
||||
|
||||
/* If an empty string or null is put for path, loads default texture */
|
||||
struct Texture *texture_pullfromfile(const char *path) {
|
||||
if (!path) return texture_notex();
|
||||
|
@ -110,7 +116,13 @@ struct Texture *texture_pullfromfile(const char *path) {
|
|||
tex->height = qoi.height;
|
||||
n = qoi.channels;
|
||||
} else if (!strcmp(ext, ".gif")) {
|
||||
data = stbi_load_gif_from_memory(raw, rawlen, NULL, &tex->width, &tex->height, &tex->frames, &n, 4);
|
||||
data = stbi_load_gif_from_memory(raw, rawlen, &tex->delays, &tex->width, &tex->height, &tex->frames, &n, 4);
|
||||
int *dd = tex->delays;
|
||||
tex->delays = NULL;
|
||||
arrsetlen(tex->delays, tex->frames);
|
||||
for (int i = 0; i < tex->frames;i++)
|
||||
tex->delays[i] = dd[i];
|
||||
free(dd);
|
||||
tex->height *= tex->frames;
|
||||
} else if (!strcmp(ext, ".svg")) {
|
||||
#ifndef NSVG
|
||||
|
|
|
@ -31,8 +31,11 @@ struct Texture {
|
|||
unsigned char *data;
|
||||
struct TextureOptions opts;
|
||||
int frames;
|
||||
int *delays;
|
||||
};
|
||||
|
||||
typedef struct Texture texture;
|
||||
|
||||
struct Image {
|
||||
struct Texture *tex;
|
||||
struct glrect frame;
|
||||
|
@ -47,6 +50,7 @@ void texture_sync(const char *path);
|
|||
char * tex_get_path(struct Texture *tex); // Get image path for texture
|
||||
|
||||
int gif_nframes(const char *path);
|
||||
int *gif_delays(const char *path);
|
||||
|
||||
struct glrect tex_get_rect(struct Texture *tex);
|
||||
HMM_Vec2 tex_get_dimensions(struct Texture *tex);
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "resources.h"
|
||||
#include "spline.h"
|
||||
#include <stdio.h>
|
||||
#include "particle.h"
|
||||
|
||||
#include "datastream.h"
|
||||
|
||||
|
@ -101,6 +102,11 @@ void c_init() {
|
|||
window_set_icon("icons/moon.gif");
|
||||
window_resize(sapp_width(), sapp_height());
|
||||
script_evalf("Game.init();");
|
||||
|
||||
particle_init();
|
||||
// emitter *e = make_emitter();
|
||||
// e->texture = texture_pullfromfile("bolt.gif");
|
||||
// start_emitter(e);
|
||||
}
|
||||
|
||||
int frame_fps() { return 1.0/sapp_frame_duration(); }
|
||||
|
@ -114,10 +120,7 @@ static void process_frame()
|
|||
/* Timers all update every frame - once per monitor refresh */
|
||||
timer_update(elapsed, timescale);
|
||||
|
||||
/* Update at a high level::
|
||||
* Update scene graph
|
||||
*
|
||||
*/
|
||||
emitters_step(elapsed);
|
||||
|
||||
if (sim_play == SIM_PLAY || sim_play == SIM_STEP) {
|
||||
if (stm_sec(stm_diff(frame_t, updatelast)) > updateMS) {
|
||||
|
|
19
source/shaders/bezier.sglsl
Normal file
19
source/shaders/bezier.sglsl
Normal file
|
@ -0,0 +1,19 @@
|
|||
@vs vs
|
||||
in vec2 pos;
|
||||
|
||||
uniform vs_p { mat4 proj; };
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = proj * vec4(pos,0.0,1.0);
|
||||
}
|
||||
@end
|
||||
|
||||
@fs fs
|
||||
void main()
|
||||
{
|
||||
|
||||
}
|
||||
@end
|
||||
|
||||
@program particle vs fs
|
45
source/shaders/particle.sglsl
Normal file
45
source/shaders/particle.sglsl
Normal file
|
@ -0,0 +1,45 @@
|
|||
@vs vs
|
||||
in vec2 vertex;
|
||||
in vec2 apos;
|
||||
in float angle;
|
||||
in float scale;
|
||||
in vec4 vc;
|
||||
|
||||
out vec4 fcolor;
|
||||
out vec2 uv;
|
||||
|
||||
uniform vs_p { mat4 proj; };
|
||||
|
||||
void main()
|
||||
{
|
||||
fcolor = vc;
|
||||
uv = vertex;
|
||||
vec2 v = vertex - 0.5;
|
||||
vec2 p = vec2(
|
||||
cos(angle)*v.x-sin(angle)*v.y,
|
||||
sin(angle)*v.x+cos(angle)*v.y
|
||||
);
|
||||
p += 0.5;
|
||||
p *= scale;
|
||||
p += apos;
|
||||
|
||||
gl_Position = proj * vec4(p, 0.0, 1.0);
|
||||
}
|
||||
@end
|
||||
|
||||
@fs fs
|
||||
in vec4 fcolor;
|
||||
out vec4 color;
|
||||
|
||||
in vec2 uv;
|
||||
uniform texture2D image;
|
||||
uniform sampler smp;
|
||||
|
||||
void main()
|
||||
{
|
||||
color = texture(sampler2D(image,smp),uv);
|
||||
color *= fcolor;
|
||||
}
|
||||
@end
|
||||
|
||||
@program particle vs fs
|
Loading…
Reference in a new issue