Laggy particle system (hold y for demo)
This commit is contained in:
parent
9d710da274
commit
afeb4a957d
13
assets/shaders/particles.fs
Normal file
13
assets/shaders/particles.fs
Normal file
@ -0,0 +1,13 @@
|
||||
precision mediump float;
|
||||
|
||||
varying vec2 uv1;
|
||||
varying vec2 uv2;
|
||||
varying float blend;
|
||||
|
||||
uniform sampler2D texture0;
|
||||
|
||||
void main() {
|
||||
vec4 color1 = texture2D(texture0, uv1);
|
||||
vec4 color2 = texture2D(texture0, uv2);
|
||||
gl_FragColor = mix(color1, color2, blend);
|
||||
}
|
28
assets/shaders/particles.vs
Normal file
28
assets/shaders/particles.vs
Normal file
@ -0,0 +1,28 @@
|
||||
precision mediump float;
|
||||
|
||||
attribute vec2 aVertexPosition;
|
||||
|
||||
uniform mat4 uModelMatrix;
|
||||
uniform mat4 uViewMatrix;
|
||||
uniform mat4 uProjectionMatrix;
|
||||
|
||||
varying vec2 uv1;
|
||||
varying vec2 uv2;
|
||||
varying float blend;
|
||||
|
||||
uniform vec2 uAtlasOffset1;
|
||||
uniform vec2 uAtlasOffset2;
|
||||
uniform vec2 uAtlasInfo;
|
||||
|
||||
void main() {
|
||||
|
||||
vec2 uv = aVertexPosition + vec2(0.5, 0.5);
|
||||
uv.y = 1.0 - uv.y;
|
||||
uv /= uAtlasInfo.x;
|
||||
uv1 = uv + uAtlasOffset1;
|
||||
uv2 = uv + uAtlasOffset2;
|
||||
blend = uAtlasInfo.y;
|
||||
|
||||
vec4 worldPosition = uModelMatrix * vec4(aVertexPosition, 0.0, 1.0);
|
||||
gl_Position = uProjectionMatrix * uViewMatrix * worldPosition;
|
||||
}
|
BIN
assets/textures/particleAtlas.png
Normal file
BIN
assets/textures/particleAtlas.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
159
src/engine/components/particles/index.js
Normal file
159
src/engine/components/particles/index.js
Normal file
@ -0,0 +1,159 @@
|
||||
import { Mesh } from '../../mesh'
|
||||
import { mat4 } from 'gl-matrix'
|
||||
|
||||
class ParticleTexture {
|
||||
constructor (texture, rows) {
|
||||
this.texture = texture
|
||||
this.rows = rows
|
||||
}
|
||||
|
||||
get id () {
|
||||
return this.texture.id
|
||||
}
|
||||
}
|
||||
|
||||
class Particle {
|
||||
constructor (texture, pos, vel, gravity, life, rot, scale) {
|
||||
this.texture = texture
|
||||
this.pos = pos
|
||||
this.vel = vel
|
||||
this.gravity = gravity
|
||||
this.life = life
|
||||
this.rot = rot
|
||||
this.scale = scale
|
||||
this.elapsed = 0
|
||||
|
||||
this.texOffset1 = [0.0, 0.0]
|
||||
this.texOffset2 = [0.0, 0.0]
|
||||
this.texBlend = 0.0
|
||||
|
||||
this.distance = 0
|
||||
}
|
||||
|
||||
_createOffset (index) {
|
||||
let col = Math.floor(index % this.texture.rows)
|
||||
let row = Math.floor(index / this.texture.rows)
|
||||
return [ col / this.texture.rows, row / this.texture.rows ]
|
||||
}
|
||||
|
||||
_updateTexture () {
|
||||
let lifeFactor = this.elapsed / this.life
|
||||
let stages = this.texture.rows * this.texture.rows
|
||||
let progression = lifeFactor * stages
|
||||
let index1 = Math.floor(progression)
|
||||
let index2 = (index1 < stages - 1) ? index1 + 1 : index1
|
||||
this.texBlend = progression % 1
|
||||
this.texOffset1 = this._createOffset(index1)
|
||||
this.texOffset2 = this._createOffset(index2)
|
||||
}
|
||||
|
||||
update (dt, gravity) {
|
||||
this.vel[1] += gravity * this.gravity * dt
|
||||
let change = [this.vel[0] * dt, this.vel[1] * dt, this.vel[2] * dt]
|
||||
this.pos = [this.pos[0] + change[0], this.pos[1] + change[1], this.pos[2] + change[2]]
|
||||
this._updateTexture()
|
||||
this.elapsed += dt
|
||||
return this.elapsed < this.life
|
||||
}
|
||||
}
|
||||
|
||||
class ParticleSystem {
|
||||
constructor (renderer) {
|
||||
this.renderer = renderer
|
||||
this.particles = []
|
||||
}
|
||||
|
||||
add (particle) {
|
||||
this.particles.push(particle)
|
||||
}
|
||||
|
||||
update (dt, gravity) {
|
||||
let alive = []
|
||||
for (let i in this.particles) {
|
||||
let particle = this.particles[i]
|
||||
let stillAlive = particle.update(dt, gravity)
|
||||
if (!stillAlive) continue
|
||||
alive.push(particle)
|
||||
}
|
||||
this.particles = alive
|
||||
}
|
||||
|
||||
draw (gl, cam) {
|
||||
if (!this.renderer) return
|
||||
this.renderer.draw(gl, this, cam)
|
||||
}
|
||||
}
|
||||
|
||||
class ParticleRenderer {
|
||||
async initialize (game) {
|
||||
this.shader = await game.shaders.createShaderFromFiles(game.gl, 'particles', false)
|
||||
this.createMesh(game.gl)
|
||||
}
|
||||
|
||||
createMesh (gl) {
|
||||
this.mesh = Mesh.constructFromVertices(gl, [-0.5, 0.5, -0.5, -0.5, 0.5, 0.5, 0.5, -0.5], 2)
|
||||
}
|
||||
|
||||
createModelMatrix (shader, viewMatrix, particle) {
|
||||
let modelMatrix = mat4.create()
|
||||
mat4.translate(modelMatrix, modelMatrix, particle.pos)
|
||||
modelMatrix[0] = viewMatrix[0]
|
||||
modelMatrix[1] = viewMatrix[4]
|
||||
modelMatrix[2] = viewMatrix[8]
|
||||
modelMatrix[4] = viewMatrix[1]
|
||||
modelMatrix[5] = viewMatrix[5]
|
||||
modelMatrix[6] = viewMatrix[9]
|
||||
modelMatrix[8] = viewMatrix[2]
|
||||
modelMatrix[10] = viewMatrix[10]
|
||||
mat4.rotate(modelMatrix, modelMatrix, particle.rot * Math.PI / 180, [0.0, 0.0, 1.0])
|
||||
mat4.scale(modelMatrix, modelMatrix, [particle.scale, particle.scale, particle.scale])
|
||||
return modelMatrix
|
||||
}
|
||||
|
||||
draw (gl, particles, cam) {
|
||||
particles = (particles instanceof ParticleSystem) ? particles.particles : particles
|
||||
let textures = []
|
||||
for (let i in particles) {
|
||||
let particle = particles[i]
|
||||
if (textures.indexOf(particle.texture) !== -1) continue
|
||||
textures.push(particle.texture)
|
||||
}
|
||||
|
||||
this.shader.use(gl)
|
||||
const modelLoc = this.shader.getUniformLocation(gl, 'uModelMatrix')
|
||||
const off1Loc = this.shader.getUniformLocation(gl, 'uAtlasOffset1')
|
||||
const off2Loc = this.shader.getUniformLocation(gl, 'uAtlasOffset2')
|
||||
const atlasLoc = this.shader.getUniformLocation(gl, 'uAtlasInfo')
|
||||
|
||||
cam.draw(gl, this.shader)
|
||||
|
||||
gl.enable(gl.BLEND)
|
||||
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
|
||||
gl.depthMask(false)
|
||||
|
||||
this.mesh.prepare(gl, this.shader)
|
||||
for (let i in textures) {
|
||||
let texture = textures[i]
|
||||
gl.activeTexture(gl.TEXTURE0)
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture.id)
|
||||
for (let j in particles) {
|
||||
let particle = particles[j]
|
||||
if (particle.texture !== texture) continue
|
||||
|
||||
let mat = this.createModelMatrix(this.shader, cam.view, particle)
|
||||
gl.uniformMatrix4fv(modelLoc, false, mat)
|
||||
gl.uniform2fv(off1Loc, particle.texOffset1)
|
||||
gl.uniform2fv(off2Loc, particle.texOffset2)
|
||||
gl.uniform2fv(atlasLoc, [texture.rows, particle.texBlend])
|
||||
|
||||
this.mesh.draw(gl, this.shader, gl.TRIANGLE_STRIP)
|
||||
}
|
||||
}
|
||||
this.mesh.postdraw(gl, this.shader)
|
||||
|
||||
gl.disable(gl.BLEND)
|
||||
gl.depthMask(true)
|
||||
}
|
||||
}
|
||||
|
||||
export { ParticleTexture, Particle, ParticleSystem, ParticleRenderer }
|
@ -115,11 +115,11 @@ class WaterTile extends Node {
|
||||
}
|
||||
|
||||
async useDUDVMap (gl, file) {
|
||||
this.dudvMap = await Texture.createTexture2D(gl, await Resource.loadImage(file + '.png'), false, gl.LINEAR)
|
||||
this.dudvMap = await Texture.fromFile(gl, file + '.png', false, gl.LINEAR)
|
||||
}
|
||||
|
||||
async useNormalMap (gl, file) {
|
||||
this.normalMap = await Texture.createTexture2D(gl, await Resource.loadImage(file + '.png'), false, gl.LINEAR)
|
||||
this.normalMap = await Texture.fromFile(gl, file + '.png', false, gl.LINEAR)
|
||||
}
|
||||
|
||||
reflect (gl, cam, render) {
|
||||
|
@ -58,12 +58,12 @@ class Engine {
|
||||
}
|
||||
|
||||
update (dt) {
|
||||
this.input.update()
|
||||
|
||||
// Updates
|
||||
for (let i in this.ust) {
|
||||
this.ust[i](dt)
|
||||
}
|
||||
|
||||
this.input.update()
|
||||
}
|
||||
|
||||
step () {
|
||||
|
@ -1,7 +1,12 @@
|
||||
import Resource from '../resource'
|
||||
|
||||
class Texture {
|
||||
static createTexture2D (gl, img, flip = true, filtering, repeat = gl.REPEAT) {
|
||||
static async fromFile (gl, file, flip, filtering, repeat) {
|
||||
let image = await Resource.loadImage(file)
|
||||
return Texture.createTexture2D(gl, image, flip, filtering, repeat)
|
||||
}
|
||||
|
||||
static createTexture2D (gl, img, flip = false, filtering, repeat = gl.REPEAT) {
|
||||
let tex = gl.createTexture()
|
||||
gl.bindTexture(gl.TEXTURE_2D, tex)
|
||||
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flip)
|
||||
@ -63,8 +68,7 @@ class Material {
|
||||
for (let ti in this.textures) {
|
||||
let tex = this.textures[ti]
|
||||
if (!(tex instanceof Texture)) {
|
||||
let teximg = await Resource.loadImage(tex)
|
||||
let result = Texture.createTexture2D(gl, teximg)
|
||||
let result = await Texture.fromFile(gl, tex, true)
|
||||
this.textures[ti] = result
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,9 @@
|
||||
import { vec3 } from 'gl-matrix'
|
||||
|
||||
export function randomInt (min, max) {
|
||||
return Math.floor(Math.random() * (max - min + 1) + min)
|
||||
}
|
||||
|
||||
export function clamp (num, min, max) {
|
||||
return Math.min(Math.max(num, min), max)
|
||||
}
|
||||
|
30
src/index.js
30
src/index.js
@ -1,12 +1,14 @@
|
||||
import Engine from './engine'
|
||||
import Camera from './engine/camera'
|
||||
import Resource from './engine/resource'
|
||||
import loadMesh from './engine/mesh/loader'
|
||||
|
||||
import { randomInt } from './engine/utility'
|
||||
|
||||
import { Environment } from './engine/environment'
|
||||
import { LODTerrain } from './engine/components/terrain/lod'
|
||||
import { Skybox } from './engine/components/skybox'
|
||||
import { WaterTile } from './engine/components/water'
|
||||
import { Particle, ParticleTexture, ParticleSystem, ParticleRenderer } from './engine/components/particles'
|
||||
import { SimplexHeightMap } from './engine/components/terrain/heightmap'
|
||||
import { Material, Texture } from './engine/mesh/material'
|
||||
import { GUIRenderer, GUIImage, Dim4 } from './engine/gui'
|
||||
@ -16,6 +18,7 @@ let game = Engine
|
||||
let env = new Environment()
|
||||
let gui = new GUIRenderer()
|
||||
let fnt = new FontRenderer()
|
||||
let prt = new ParticleRenderer()
|
||||
|
||||
async function pipeline () {
|
||||
let entity = await loadMesh(game.gl, 'test')
|
||||
@ -38,8 +41,15 @@ async function pipeline () {
|
||||
await gui.initialize(game)
|
||||
await fnt.initialize(game)
|
||||
|
||||
// Initialize particles
|
||||
await prt.initialize(game)
|
||||
let particleSystem = new ParticleSystem(prt)
|
||||
|
||||
// Particle texture atlas
|
||||
let particleTexture = new ParticleTexture(await Texture.fromFile(game.gl, 'particleAtlas.png'), 4)
|
||||
|
||||
let itms = [
|
||||
new GUIImage(await Texture.createTexture2D(game.gl, await Resource.loadImage('noisy.png'), false, game.gl.LINEAR),
|
||||
new GUIImage(await Texture.fromFile(game.gl, 'noisy.png', false, game.gl.LINEAR),
|
||||
new Dim4(-0.9, 0.0, 0.9, 0.0), new Dim4(0.1, 0.0, 0.1, 0.0))
|
||||
]
|
||||
// Nesting test
|
||||
@ -94,6 +104,19 @@ async function pipeline () {
|
||||
cam.processMouseMove(game.input.mouseOffset)
|
||||
}
|
||||
|
||||
// Particles
|
||||
particleSystem.update(dt, -50)
|
||||
|
||||
if (game.input.isDown('y')) {
|
||||
let velocity = 20
|
||||
for (let i = 0; i < 360; i += 15) {
|
||||
let rad1 = i * Math.PI / 180
|
||||
let x1 = (Math.cos(rad1) * velocity) + randomInt(-5, 5)
|
||||
let y1 = (Math.sin(rad1) * velocity) + randomInt(-5, 5)
|
||||
particleSystem.add(new Particle(particleTexture, [0.0, 0.0, 0.0], [x1, randomInt(-velocity, velocity), y1], 0.2, 2, randomInt(0, 360), randomInt(0.1, 1)))
|
||||
}
|
||||
}
|
||||
|
||||
// Update detail levels
|
||||
terrain.update(game.gl, cam)
|
||||
terrain.updateLODMesh(game.gl)
|
||||
@ -136,6 +159,9 @@ async function pipeline () {
|
||||
cam.draw(gl, waterShader)
|
||||
water.draw(gl, waterShader, cam, env.sun)
|
||||
|
||||
// Draw particles
|
||||
particleSystem.draw(gl, cam)
|
||||
|
||||
// Draw GUIs
|
||||
gui.draw(gl, cam, itms)
|
||||
fnt.draw(gl, cam, itms)
|
||||
|
Loading…
Reference in New Issue
Block a user