Laggy particle system (hold y for demo)

This commit is contained in:
Evert Prants 2019-12-31 21:00:46 +02:00
parent 9d710da274
commit afeb4a957d
Signed by: evert
GPG Key ID: 1688DA83D222D0B5
9 changed files with 243 additions and 9 deletions

View 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);
}

View 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;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View 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 }

View File

@ -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) {

View File

@ -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 () {

View File

@ -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
}
}

View File

@ -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)
}

View File

@ -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)