Add font rendering

This commit is contained in:
Evert Prants 2019-12-29 04:20:57 +02:00
parent 4ffd8e333f
commit e103fa6e3b
Signed by: evert
GPG Key ID: 1688DA83D222D0B5
11 changed files with 683 additions and 6 deletions

198
assets/fonts/arial.fnt Normal file
View File

@ -0,0 +1,198 @@
info face="Arial" size=82 bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=3,3,3,3 spacing=-2,-2
common lineHeight=100 base=75 scaleW=512 scaleH=512 pages=1 packed=0
page id=0 file="arial.png"
chars count=97
char id=0 x=344 y=346 width=48 height=58 xoffset=7 yoffset=20 xadvance=66 page=0 chnl=0
char id=10 x=0 y=0 width=0 height=0 xoffset=-3 yoffset=0 xadvance=4 page=0 chnl=0
char id=32 x=0 y=0 width=0 height=0 xoffset=-3 yoffset=0 xadvance=27 page=0 chnl=0
char id=33 x=118 y=346 width=13 height=65 xoffset=6 yoffset=13 xadvance=29 page=0 chnl=0
char id=34 x=250 y=462 width=28 height=27 xoffset=0 yoffset=13 xadvance=33 page=0 chnl=0
char id=35 x=191 y=346 width=49 height=65 xoffset=-2 yoffset=13 xadvance=50 page=0 chnl=0
char id=36 x=269 y=0 width=44 height=76 xoffset=0 yoffset=10 xadvance=50 page=0 chnl=0
char id=37 x=158 y=83 width=69 height=67 xoffset=2 yoffset=12 xadvance=77 page=0 chnl=0
char id=38 x=227 y=83 width=55 height=67 xoffset=1 yoffset=12 xadvance=59 page=0 chnl=0
char id=39 x=489 y=411 width=13 height=27 xoffset=1 yoffset=13 xadvance=20 page=0 chnl=0
char id=40 x=177 y=0 width=25 height=81 xoffset=2 yoffset=13 xadvance=31 page=0 chnl=0
char id=41 x=202 y=0 width=25 height=81 xoffset=0 yoffset=13 xadvance=31 page=0 chnl=0
char id=42 x=216 y=462 width=34 height=31 xoffset=0 yoffset=13 xadvance=36 page=0 chnl=0
char id=43 x=88 y=462 width=45 height=45 xoffset=1 yoffset=24 xadvance=52 page=0 chnl=0
char id=44 x=278 y=462 width=13 height=26 xoffset=4 yoffset=65 xadvance=27 page=0 chnl=0
char id=45 x=371 y=462 width=28 height=13 xoffset=0 yoffset=47 xadvance=31 page=0 chnl=0
char id=46 x=358 y=462 width=13 height=13 xoffset=4 yoffset=65 xadvance=27 page=0 chnl=0
char id=47 x=131 y=346 width=30 height=65 xoffset=-3 yoffset=13 xadvance=27 page=0 chnl=0
char id=48 x=309 y=150 width=44 height=66 xoffset=1 yoffset=13 xadvance=50 page=0 chnl=0
char id=49 x=0 y=346 width=28 height=65 xoffset=6 yoffset=13 xadvance=50 page=0 chnl=0
char id=50 x=28 y=346 width=46 height=65 xoffset=-2 yoffset=13 xadvance=50 page=0 chnl=0
char id=51 x=131 y=150 width=44 height=66 xoffset=1 yoffset=13 xadvance=50 page=0 chnl=0
char id=52 x=240 y=346 width=47 height=64 xoffset=-2 yoffset=14 xadvance=50 page=0 chnl=0
char id=53 x=74 y=346 width=44 height=65 xoffset=1 yoffset=14 xadvance=50 page=0 chnl=0
char id=54 x=175 y=150 width=46 height=66 xoffset=-1 yoffset=13 xadvance=50 page=0 chnl=0
char id=55 x=287 y=346 width=44 height=64 xoffset=1 yoffset=14 xadvance=50 page=0 chnl=0
char id=56 x=221 y=150 width=44 height=66 xoffset=1 yoffset=13 xadvance=50 page=0 chnl=0
char id=57 x=265 y=150 width=44 height=66 xoffset=1 yoffset=13 xadvance=50 page=0 chnl=0
char id=58 x=476 y=411 width=13 height=49 xoffset=4 yoffset=29 xadvance=27 page=0 chnl=0
char id=59 x=331 y=346 width=13 height=62 xoffset=4 yoffset=29 xadvance=27 page=0 chnl=0
char id=60 x=0 y=462 width=44 height=45 xoffset=2 yoffset=24 xadvance=52 page=0 chnl=0
char id=61 x=171 y=462 width=45 height=31 xoffset=1 yoffset=30 xadvance=52 page=0 chnl=0
char id=62 x=44 y=462 width=44 height=45 xoffset=2 yoffset=24 xadvance=52 page=0 chnl=0
char id=63 x=353 y=150 width=44 height=66 xoffset=1 yoffset=12 xadvance=50 page=0 chnl=0
char id=64 x=70 y=0 width=82 height=83 xoffset=1 yoffset=12 xadvance=87 page=0 chnl=0
char id=65 x=397 y=150 width=63 height=65 xoffset=-5 yoffset=13 xadvance=59 page=0 chnl=0
char id=66 x=460 y=150 width=50 height=65 xoffset=4 yoffset=13 xadvance=59 page=0 chnl=0
char id=67 x=375 y=0 width=57 height=67 xoffset=1 yoffset=12 xadvance=63 page=0 chnl=0
char id=68 x=0 y=216 width=54 height=65 xoffset=4 yoffset=13 xadvance=63 page=0 chnl=0
char id=69 x=54 y=216 width=50 height=65 xoffset=4 yoffset=13 xadvance=59 page=0 chnl=0
char id=70 x=104 y=216 width=45 height=65 xoffset=4 yoffset=13 xadvance=54 page=0 chnl=0
char id=71 x=432 y=0 width=61 height=67 xoffset=1 yoffset=12 xadvance=68 page=0 chnl=0
char id=72 x=149 y=216 width=51 height=65 xoffset=4 yoffset=13 xadvance=63 page=0 chnl=0
char id=73 x=489 y=83 width=14 height=65 xoffset=4 yoffset=13 xadvance=27 page=0 chnl=0
char id=74 x=282 y=83 width=38 height=66 xoffset=-1 yoffset=13 xadvance=45 page=0 chnl=0
char id=75 x=200 y=216 width=55 height=65 xoffset=4 yoffset=13 xadvance=59 page=0 chnl=0
char id=76 x=255 y=216 width=42 height=65 xoffset=4 yoffset=13 xadvance=50 page=0 chnl=0
char id=77 x=297 y=216 width=63 height=65 xoffset=2 yoffset=13 xadvance=71 page=0 chnl=0
char id=78 x=360 y=216 width=51 height=65 xoffset=4 yoffset=13 xadvance=63 page=0 chnl=0
char id=79 x=0 y=83 width=62 height=67 xoffset=1 yoffset=12 xadvance=68 page=0 chnl=0
char id=80 x=411 y=216 width=50 height=65 xoffset=4 yoffset=13 xadvance=59 page=0 chnl=0
char id=81 x=313 y=0 width=62 height=71 xoffset=1 yoffset=12 xadvance=68 page=0 chnl=0
char id=82 x=0 y=281 width=56 height=65 xoffset=4 yoffset=13 xadvance=63 page=0 chnl=0
char id=83 x=62 y=83 width=53 height=67 xoffset=1 yoffset=12 xadvance=59 page=0 chnl=0
char id=84 x=56 y=281 width=52 height=65 xoffset=-1 yoffset=13 xadvance=54 page=0 chnl=0
char id=85 x=320 y=83 width=51 height=66 xoffset=4 yoffset=13 xadvance=63 page=0 chnl=0
char id=86 x=108 y=281 width=63 height=65 xoffset=-4 yoffset=13 xadvance=59 page=0 chnl=0
char id=87 x=171 y=281 width=85 height=65 xoffset=-2 yoffset=13 xadvance=85 page=0 chnl=0
char id=88 x=256 y=281 width=59 height=65 xoffset=-3 yoffset=13 xadvance=58 page=0 chnl=0
char id=89 x=315 y=281 width=60 height=65 xoffset=-3 yoffset=13 xadvance=58 page=0 chnl=0
char id=90 x=375 y=281 width=52 height=65 xoffset=-1 yoffset=13 xadvance=54 page=0 chnl=0
char id=91 x=227 y=0 width=21 height=81 xoffset=2 yoffset=13 xadvance=27 page=0 chnl=0
char id=92 x=161 y=346 width=30 height=65 xoffset=-3 yoffset=13 xadvance=27 page=0 chnl=0
char id=93 x=248 y=0 width=21 height=81 xoffset=0 yoffset=13 xadvance=27 page=0 chnl=0
char id=94 x=133 y=462 width=38 height=38 xoffset=-1 yoffset=12 xadvance=40 page=0 chnl=0
char id=95 x=399 y=462 width=55 height=13 xoffset=-4 yoffset=81 xadvance=50 page=0 chnl=0
char id=96 x=338 y=462 width=20 height=17 xoffset=0 yoffset=13 xadvance=31 page=0 chnl=0
char id=97 x=392 y=346 width=46 height=51 xoffset=0 yoffset=28 xadvance=50 page=0 chnl=0
char id=98 x=371 y=83 width=43 height=66 xoffset=2 yoffset=13 xadvance=50 page=0 chnl=0
char id=99 x=438 y=346 width=43 height=51 xoffset=0 yoffset=28 xadvance=45 page=0 chnl=0
char id=100 x=414 y=83 width=43 height=66 xoffset=0 yoffset=13 xadvance=50 page=0 chnl=0
char id=101 x=0 y=411 width=46 height=51 xoffset=0 yoffset=28 xadvance=50 page=0 chnl=0
char id=102 x=457 y=83 width=32 height=66 xoffset=-1 yoffset=12 xadvance=28 page=0 chnl=0
char id=103 x=115 y=83 width=43 height=67 xoffset=0 yoffset=28 xadvance=49 page=0 chnl=0
char id=104 x=461 y=216 width=40 height=65 xoffset=3 yoffset=13 xadvance=50 page=0 chnl=0
char id=105 x=493 y=0 width=13 height=65 xoffset=3 yoffset=13 xadvance=22 page=0 chnl=0
char id=106 x=152 y=0 width=25 height=82 xoffset=-7 yoffset=13 xadvance=21 page=0 chnl=0
char id=107 x=427 y=281 width=41 height=65 xoffset=2 yoffset=13 xadvance=45 page=0 chnl=0
char id=108 x=468 y=281 width=13 height=65 xoffset=3 yoffset=13 xadvance=22 page=0 chnl=0
char id=109 x=133 y=411 width=63 height=50 xoffset=2 yoffset=28 xadvance=71 page=0 chnl=0
char id=110 x=196 y=411 width=40 height=50 xoffset=3 yoffset=28 xadvance=50 page=0 chnl=0
char id=111 x=46 y=411 width=46 height=51 xoffset=0 yoffset=28 xadvance=50 page=0 chnl=0
char id=112 x=0 y=150 width=43 height=66 xoffset=2 yoffset=28 xadvance=50 page=0 chnl=0
char id=113 x=43 y=150 width=43 height=66 xoffset=0 yoffset=28 xadvance=49 page=0 chnl=0
char id=114 x=481 y=346 width=29 height=50 xoffset=3 yoffset=28 xadvance=31 page=0 chnl=0
char id=115 x=92 y=411 width=41 height=51 xoffset=0 yoffset=28 xadvance=45 page=0 chnl=0
char id=116 x=481 y=281 width=26 height=65 xoffset=-1 yoffset=14 xadvance=27 page=0 chnl=0
char id=117 x=236 y=411 width=40 height=50 xoffset=3 yoffset=29 xadvance=50 page=0 chnl=0
char id=118 x=276 y=411 width=45 height=49 xoffset=-2 yoffset=29 xadvance=45 page=0 chnl=0
char id=119 x=321 y=411 width=66 height=49 xoffset=-4 yoffset=29 xadvance=61 page=0 chnl=0
char id=120 x=387 y=411 width=46 height=49 xoffset=-2 yoffset=29 xadvance=45 page=0 chnl=0
char id=121 x=86 y=150 width=45 height=66 xoffset=-2 yoffset=29 xadvance=43 page=0 chnl=0
char id=122 x=433 y=411 width=43 height=49 xoffset=-2 yoffset=29 xadvance=44 page=0 chnl=0
char id=123 x=0 y=0 width=29 height=83 xoffset=-1 yoffset=12 xadvance=31 page=0 chnl=0
char id=124 x=58 y=0 width=12 height=83 xoffset=4 yoffset=13 xadvance=24 page=0 chnl=0
char id=125 x=29 y=0 width=29 height=83 xoffset=-1 yoffset=12 xadvance=31 page=0 chnl=0
char id=126 x=291 y=462 width=47 height=21 xoffset=0 yoffset=37 xadvance=52 page=0 chnl=0
kernings count=96
kerning first=121 second=46 amount=-6
kerning first=84 second=45 amount=-5
kerning first=86 second=44 amount=-8
kerning first=114 second=46 amount=-5
kerning first=114 second=44 amount=-5
kerning first=49 second=49 amount=-6
kerning first=89 second=65 amount=-6
kerning first=84 second=79 amount=-1
kerning first=65 second=84 amount=-6
kerning first=76 second=86 amount=-6
kerning first=65 second=87 amount=-3
kerning first=76 second=89 amount=-6
kerning first=84 second=99 amount=-9
kerning first=86 second=101 amount=-5
kerning first=102 second=102 amount=-1
kerning first=89 second=105 amount=-3
kerning first=86 second=58 amount=-3
kerning first=86 second=111 amount=-5
kerning first=89 second=112 amount=-6
kerning first=89 second=113 amount=-8
kerning first=86 second=114 amount=-3
kerning first=89 second=117 amount=-5
kerning first=65 second=118 amount=-1
kerning first=84 second=119 amount=-5
kerning first=76 second=121 amount=-3
kerning first=82 second=84 amount=-1
kerning first=89 second=58 amount=-5
kerning first=87 second=65 amount=-3
kerning first=87 second=97 amount=-3
kerning first=87 second=59 amount=-1
kerning first=82 second=87 amount=-1
kerning first=118 second=46 amount=-6
kerning first=65 second=89 amount=-6
kerning first=65 second=86 amount=-6
kerning first=80 second=44 amount=-11
kerning first=86 second=46 amount=-8
kerning first=84 second=105 amount=-3
kerning first=84 second=97 amount=-9
kerning first=84 second=114 amount=-3
kerning first=80 second=65 amount=-6
kerning first=84 second=58 amount=-9
kerning first=86 second=97 amount=-6
kerning first=76 second=84 amount=-6
kerning first=89 second=59 amount=-5
kerning first=70 second=44 amount=-9
kerning first=80 second=46 amount=-11
kerning first=89 second=101 amount=-8
kerning first=65 second=119 amount=-1
kerning first=87 second=121 amount=-1
kerning first=76 second=87 amount=-6
kerning first=86 second=45 amount=-5
kerning first=32 second=89 amount=-1
kerning first=84 second=117 amount=-3
kerning first=89 second=118 amount=-5
kerning first=65 second=32 amount=-5
kerning first=86 second=65 amount=-6
kerning first=84 second=111 amount=-9
kerning first=89 second=45 amount=-8
kerning first=65 second=121 amount=-1
kerning first=87 second=58 amount=-1
kerning first=82 second=89 amount=-1
kerning first=89 second=44 amount=-11
kerning first=32 second=84 amount=-1
kerning first=87 second=111 amount=-1
kerning first=84 second=59 amount=-9
kerning first=84 second=101 amount=-9
kerning first=84 second=32 amount=-1
kerning first=86 second=59 amount=-3
kerning first=89 second=46 amount=-11
kerning first=87 second=101 amount=-1
kerning first=32 second=65 amount=-5
kerning first=84 second=44 amount=-9
kerning first=70 second=65 amount=-5
kerning first=86 second=117 amount=-3
kerning first=84 second=115 amount=-9
kerning first=84 second=65 amount=-6
kerning first=89 second=32 amount=-1
kerning first=87 second=44 amount=-5
kerning first=89 second=111 amount=-8
kerning first=89 second=97 amount=-6
kerning first=119 second=46 amount=-5
kerning first=87 second=46 amount=-5
kerning first=82 second=86 amount=-1
kerning first=121 second=44 amount=-6
kerning first=84 second=46 amount=-9
kerning first=80 second=32 amount=-1
kerning first=87 second=114 amount=-1
kerning first=119 second=44 amount=-5
kerning first=76 second=32 amount=-3
kerning first=84 second=121 amount=-5
kerning first=86 second=121 amount=-3
kerning first=70 second=46 amount=-9
kerning first=87 second=45 amount=-1
kerning first=118 second=44 amount=-6
kerning first=87 second=117 amount=-1
kerning first=86 second=105 amount=-1

BIN
assets/fonts/arial.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

10
assets/shaders/font.fs Normal file
View File

@ -0,0 +1,10 @@
precision mediump float;
varying vec2 uv;
uniform vec3 uColor;
uniform sampler2D texture0;
void main() {
gl_FragColor = vec4(uColor, texture2D(texture0, uv).a);
}

12
assets/shaders/font.vs Normal file
View File

@ -0,0 +1,12 @@
precision mediump float;
attribute vec2 aVertexPosition;
attribute vec2 aTexCoords;
varying vec2 uv;
uniform mat4 uTransformation;
void main() {
gl_Position = uTransformation * vec4(aVertexPosition, 0.0, 1.0);
uv = aTexCoords;
}

425
src/engine/gui/font.js Normal file
View File

@ -0,0 +1,425 @@
import Resource from '../resource'
import Screen from '../screen'
import { Mesh } from '../mesh'
import { Texture } from '../mesh/material'
import { Node2D } from './'
import { mat4 } from 'gl-matrix'
const aspectRatio = Screen.width / Screen.height
const PAD_TOP = 0
const PAD_LEFT = 1
const PAD_BOTTOM = 2
const PAD_RIGHT = 3
const DESIRED_PADDING = 3
const SPLITTER = ' '
const NUMBER_SEPARATOR = ','
const LINE_HEIGHT = 0.03
const SPACE_ASCII = 32
const NL_ASCII = 10
class Character {
constructor (id, xTexCoord, yTexCoord, xTexSize, yTexSize,
xOffset, yOffset, sizeX, sizeY, xAdvance) {
this.id = id
this.xTexCoord = xTexCoord
this.yTexCoord = yTexCoord
this.xTexSize = xTexSize
this.yTexSize = yTexSize
this.xOffset = xOffset
this.yOffset = yOffset
this.sizeX = sizeX
this.sizeY = sizeY
this.xMaxTexCoord = xTexSize + xTexCoord
this.yMaxTexCoord = yTexSize + yTexCoord
this.xAdvance = xAdvance
}
}
class Word {
constructor (fontSize) {
this.fontSize = fontSize
this.characters = []
this.width = 0
}
addCharacter (char) {
this.characters.push(char)
this.width += char.xAdvance * this.fontSize
}
}
class Line {
constructor (spaceWidth, fontSize, maxLength) {
this.spaceSize = spaceWidth * fontSize
this.maxLength = maxLength
this.words = []
this.lineLength = 0
}
attemptToAddWord (word) {
let additionalLength = word.width
additionalLength += this.words.length ? this.spaceSize : 0
if (this.lineLength + additionalLength <= this.maxLength) {
this.words.push(word)
this.lineLength += additionalLength
return true
}
return false
}
}
class FontFile {
constructor (name) {
this.name = name
this.values = {}
this.metadata = {}
}
getValue (vals, key) {
return parseInt(vals[key])
}
getValues (vals, key) {
let nums = vals[key].split(NUMBER_SEPARATOR)
let result = []
for (let i in nums) {
result.push(parseInt(nums[i]))
}
return result
}
readValues (data) {
let lines = data.split('\n')
for (let i in lines) {
let line = lines[i]
let lineSplit = line.split(SPLITTER)
let lineValues = {}
for (let j in lineSplit) {
let valuePairs = lineSplit[j].split('=')
if (valuePairs.length === 2) {
let key = valuePairs[0]
let val = valuePairs[1]
lineValues[key] = val
}
}
if (lineSplit[0] === 'info') {
this.padding = this.getValues(lineValues, 'padding')
this.paddingWidth = this.padding[PAD_LEFT] + this.padding[PAD_RIGHT]
this.paddingHeight = this.padding[PAD_TOP] + this.padding[PAD_BOTTOM]
} else if (lineSplit[0] === 'common') {
let lineHeightPixels = this.getValue(lineValues, 'lineHeight') - this.paddingHeight
this.vertPerPixelSize = LINE_HEIGHT / lineHeightPixels
this.horizPixelSize = this.vertPerPixelSize / aspectRatio
} else if (lineSplit[0] === 'char') {
let c = this.loadCharacter(lineValues, this.getValue(this.values, 'scaleW'))
if (c) this.metadata[c.id] = c
continue
} else if (lineSplit[0] === 'kernings' || lineSplit[0] === 'kerning' || lineSplit[0] === 'chars') {
continue
}
for (let j in lineValues) {
this.values[j] = lineValues[j]
}
}
}
loadCharacter (values, imageSize) {
let id = this.getValue(values, 'id')
if (id === SPACE_ASCII) {
this.spaceWidth = (this.getValue(values, 'xadvance') - this.paddingWidth) * this.horizPixelSize
return null
}
let xTex = (this.getValue(values, 'x') + (this.padding[PAD_LEFT] - DESIRED_PADDING)) / imageSize
let yTex = (this.getValue(values, 'y') + (this.padding[PAD_TOP] - DESIRED_PADDING)) / imageSize
let width = this.getValue(values, 'width') - (this.paddingWidth - (2 * DESIRED_PADDING))
let height = this.getValue(values, 'height') - ((this.paddingHeight) - (2 * DESIRED_PADDING))
let quadWidth = width * this.horizPixelSize
let quadHeight = height * this.vertPerPixelSize
let xTexSize = width / imageSize
let yTexSize = height / imageSize
let xOff = (this.getValue(values, 'xoffset') + this.padding[PAD_LEFT] - DESIRED_PADDING) * this.horizPixelSize
let yOff = (this.getValue(values, 'yoffset') + (this.padding[PAD_TOP] - DESIRED_PADDING)) * this.vertPerPixelSize
let xAdvance = (this.getValue(values, 'xadvance') - this.paddingWidth) * this.horizPixelSize
return new Character(id, xTex, yTex, xTexSize, yTexSize, xOff, yOff, quadWidth, quadHeight, xAdvance)
}
getCharacter (ascii) {
return this.metadata[ascii]
}
static async fromFile (fontName) {
let load = await Resource.GET('/assets/fonts/' + fontName + '.fnt')
let file = new FontFile(fontName)
file.readValues(load)
console.log(file)
return file
}
}
class Font {
constructor (name, metadata) {
this.name = name
this.metadata = metadata
this.texture = null
}
static async fromFile (name) {
let meta = await FontFile.fromFile(name)
return new Font(name, meta)
}
async loadTextures (gl) {
this.texture = await Texture.createTexture2D(gl, await Resource.loadImage('/assets/fonts/' + this.name + '.png'), false, gl.LINEAR)
}
createTextMesh (gl, text) {
let lines = this.createStructure(text)
let data = this.createQuadVertices(text, lines)
return Mesh.constructFromVerticesUVs(gl, data.vertices, data.textureCoords)
}
createStructure (text) {
let chars = text.asCharacters
let lines = []
let currentLine = new Line(this.metadata.spaceWidth, text.fontSize, text.lineLength)
let currentWord = new Word(text.fontSize)
for (let c in chars) {
let ascii = parseInt(chars[c])
if (ascii === SPACE_ASCII) {
let added = currentLine.attemptToAddWord(currentWord)
if (!added) {
lines.push(currentLine)
currentLine = new Line(this.metadata.spaceWidth, text.fontSize, text.lineLength)
currentLine.attemptToAddWord(currentWord)
}
currentWord = new Word(text.fontSize)
continue
}
if (ascii === NL_ASCII) {
let added = currentLine.attemptToAddWord(currentWord)
if (!added) {
lines.push(currentLine)
currentLine = new Line(this.metadata.spaceWidth, text.fontSize, text.lineLength)
currentLine.attemptToAddWord(currentWord)
}
lines.push(currentLine)
currentLine = new Line(this.metadata.spaceWidth, text.fontSize, text.lineLength)
currentWord = new Word(text.fontSize)
continue
}
let character = this.metadata.getCharacter(ascii)
currentWord.addCharacter(character)
}
this.completeStructure(lines, currentLine, currentWord, text)
return lines
}
completeStructure (lines, currentLine, currentWord, text) {
let added = currentLine.attemptToAddWord(currentWord)
if (!added) {
lines.push(currentLine)
currentLine = new Line(this.metadata.spaceWidth, text.fontSize, text.lineLength)
currentLine.attemptToAddWord(currentWord)
}
lines.push(currentLine)
return lines
}
createQuadVertices (text, lines) {
text.lines = lines.length
let cursorX = 0
let cursorY = 0
let vertices = []
let textureCoords = []
for (let i in lines) {
let line = lines[i]
if (text.centered) {
cursorX = (line.maxLength - line.lineLength) / 2
}
for (let j in line.words) {
let word = line.words[j]
for (let k in word.characters) {
let letter = word.characters[k]
this.addVerticesForCharacter(cursorX, cursorY, letter, text.fontSize, vertices)
this.addTexCoords(textureCoords, letter.xTexCoord, letter.yTexCoord, letter.xMaxTexCoord, letter.yMaxTexCoord)
cursorX += letter.xAdvance * text.fontSize
}
cursorX += this.metadata.spaceWidth * text.fontSize
}
cursorX = 0
cursorY += LINE_HEIGHT * text.fontSize
}
return { vertices, textureCoords }
}
addVerticesForCharacter (cursorX, cursorY, character, fontSize, vertices) {
let x = cursorX + (character.xOffset * fontSize)
let y = cursorY + (character.yOffset * fontSize)
let maxX = x + (character.sizeX * fontSize)
let maxY = y + (character.sizeY * fontSize)
let properX = (2 * x) - 1
let properY = (-2 * y) + 1
let properMaxX = (2 * maxX) - 1
let properMaxY = (-2 * maxY) + 1
this.addVertices(vertices, properX, properY, properMaxX, properMaxY)
}
addVertices (vertices, x, y, maxX, maxY) {
vertices.push(x)
vertices.push(y)
vertices.push(x)
vertices.push(maxY)
vertices.push(maxX)
vertices.push(maxY)
vertices.push(maxX)
vertices.push(maxY)
vertices.push(maxX)
vertices.push(y)
vertices.push(x)
vertices.push(y)
}
addTexCoords (texCoords, x, y, maxX, maxY) {
texCoords.push(x)
texCoords.push(y)
texCoords.push(x)
texCoords.push(maxY)
texCoords.push(maxX)
texCoords.push(maxY)
texCoords.push(maxX)
texCoords.push(maxY)
texCoords.push(maxX)
texCoords.push(y)
texCoords.push(x)
texCoords.push(y)
}
}
class GUIText extends Node2D {
constructor (text, font, fontSize, pos, size, centered = false) {
super(pos, size)
this.text = text
this.fontSize = fontSize
this.font = font
this.centered = centered
this.color = [0.0, 0.0, 0.0]
}
updateTransform () {
let matrix = mat4.create()
mat4.translate(matrix, matrix, [this.pos[0], this.pos[2], 0.0])
if (this.rotation !== 0.0) {
mat4.rotate(matrix, matrix, this.rotation * Math.PI / 180, [0.0, 0.0, 1.0])
}
// Add parent's transform to this
if (this.parent) {
mat4.mul(matrix, this.parent.transform, matrix)
}
// Set the matrix
this.transform = matrix
// Update children's transforms
for (let i in this.children) {
let child = this.children[i]
if (!(child instanceof Node2D)) continue
child.updateTransform()
}
}
createMesh (gl) {
this.mesh = this.font.createTextMesh(gl, this)
}
get lineLength () {
return this.size[2]
}
get asCharacters () {
let chars = []
for (let i = 0; i < this.text.length; i++) {
chars.push(this.text.charCodeAt(i))
}
return chars
}
draw (gl, shader) {
const transformLocation = shader.getUniformLocation(gl, 'uTransformation')
const colorLocation = shader.getUniformLocation(gl, 'uColor')
gl.uniformMatrix4fv(transformLocation, false, this.transform)
gl.uniform3fv(colorLocation, this.color)
if (!this.mesh) this.createMesh(gl)
this.mesh.prepare(gl, shader)
this.mesh.draw(gl, shader)
this.mesh.postdraw(gl, shader)
}
}
class FontRenderer {
discoverTextNodes (nodes) {
let textNodes = []
for (let i in nodes) {
let node = nodes[i]
if (!(node instanceof GUIText)) {
if (node.children) {
textNodes.concat(this.discoverTextNodes(node.children))
}
continue
}
textNodes.push(node)
}
return textNodes
}
draw (gl, nodes) {
let fontPairs = {}
let textNodes = this.discoverTextNodes(nodes)
for (let i in textNodes) {
let node = textNodes[i]
if (!this.fonts) {
this.fonts = {}
}
if (!this.fonts[node.font.name]) {
this.fonts[node.font.name] = node.font
}
if (fontPairs[node.font.name]) {
fontPairs[node.font.name].push(node)
} else {
fontPairs[node.font.name] = [node]
}
}
this.shader.use(gl)
gl.enable(gl.BLEND)
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
gl.disable(gl.DEPTH_TEST)
for (let i in fontPairs) {
let texts = fontPairs[i]
let font = this.fonts[i]
gl.activeTexture(gl.TEXTURE0)
gl.bindTexture(font.texture.type, font.texture.id)
for (let j in texts) {
let text = texts[j]
text.draw(gl, this.shader)
}
}
gl.enable(gl.DEPTH_TEST)
gl.disable(gl.BLEND)
}
async initialize (game) {
this.shader = await game.shaders.createShaderFromFiles(game.gl, 'font', false)
}
}
export { FontRenderer, GUIText, Font }

View File

@ -81,7 +81,7 @@ class Node2D {
// Scaling // Scaling
this.gscale = [1.0, 1.0] this.gscale = [1.0, 1.0]
this.size = size || new Dim4(0.0, 0.0, 0.0, 0.0) this.size = size || new Dim4(1.0, 0.0, 1.0, 0.0)
// Rotation in degrees // Rotation in degrees
this.rotation = rotation || 0.0 this.rotation = rotation || 0.0
@ -214,4 +214,4 @@ class GUIRenderer {
} }
} }
export { Dim4, GUIRenderer, GUIImage } export { Node2D, Dim4, GUIRenderer, GUIImage }

View File

@ -7,7 +7,7 @@ let gl
class Engine { class Engine {
constructor () { constructor () {
this.screen = new Screen() this.screen = Screen
this.input = new Input(this.screen.gl.canvas) this.input = new Input(this.screen.gl.canvas)
this.shaders = new ShaderManager() this.shaders = new ShaderManager()
this.running = false this.running = false

View File

@ -50,6 +50,20 @@ class Mesh {
return mesh return mesh
} }
static constructFromVerticesUVs (gl, vertices, uvs, dimensions = 2) {
let pos = Mesh.loadToBuffer(gl, gl.ARRAY_BUFFER, new Float32Array(vertices))
let mesh = new Mesh()
mesh.uv = uvs
mesh.posBuffer = pos
mesh.vertices = vertices
mesh.vertexCount = vertices.length / dimensions
mesh.vertexLayout = dimensions
mesh.uvs = Mesh.loadToBuffer(gl, gl.ARRAY_BUFFER, new Float32Array(uvs))
gl.bindBuffer(gl.ARRAY_BUFFER, null)
return mesh
}
bindBuffers (gl, shader) { bindBuffers (gl, shader) {
this._bufferCount = 1 this._bufferCount = 1

View File

@ -53,7 +53,7 @@ function smartGET (data) {
} }
function loadImage (url) { function loadImage (url) {
url = '/assets/textures/' + url if (url.indexOf('/') !== 0) url = '/assets/textures/' + url
// Ensure we don't load a texture multiple times // Ensure we don't load a texture multiple times
if (imgCache[url]) return imgCache[url] if (imgCache[url]) return imgCache[url]

View File

@ -30,6 +30,14 @@ class Screen {
this._el.width = window.innerWidth this._el.width = window.innerWidth
this._el.height = window.innerHeight this._el.height = window.innerHeight
} }
get width () {
return this._el.width
}
get height () {
return this._el.height
}
} }
export default Screen export default new Screen()

View File

@ -9,10 +9,12 @@ import { Skybox } from './engine/components/skybox'
import { SimplexHeightMap } from './engine/components/terrain/heightmap' import { SimplexHeightMap } from './engine/components/terrain/heightmap'
import { Material, Texture } from './engine/mesh/material' import { Material, Texture } from './engine/mesh/material'
import { GUIRenderer, GUIImage, Dim4 } from './engine/gui' import { GUIRenderer, GUIImage, Dim4 } from './engine/gui'
import { FontRenderer, GUIText, Font } from './engine/gui/font'
let game = new Engine() let game = new Engine()
let env = new Environment() let env = new Environment()
let gui = new GUIRenderer() let gui = new GUIRenderer()
let fnt = new FontRenderer()
async function pipeline () { async function pipeline () {
let entity = await loadMesh(game.gl, 'test') let entity = await loadMesh(game.gl, 'test')
@ -22,12 +24,19 @@ async function pipeline () {
entity.setRotation([0.0, 0.0, -90.0]) entity.setRotation([0.0, 0.0, -90.0])
let arialFont = await Font.fromFile('arial')
await arialFont.loadTextures(game.gl)
// Initialize GUI // Initialize GUI
await gui.initialize(game) await gui.initialize(game)
await fnt.initialize(game)
let itms = [ let itms = [
new GUIImage(await Texture.createTexture2D(game.gl, await Resource.loadImage('noisy.png'), false, game.gl.LINEAR), new GUIImage(await Texture.createTexture2D(game.gl, await Resource.loadImage('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)) new Dim4(-0.9, 0.0, 0.9, 0.0), new Dim4(0.1, 0.0, 0.1, 0.0)),
new GUIText('this is example text!\nmulti line!', arialFont, 2, new Dim4(0.1, 0.0, -0.2, 0.0), new Dim4(1.0, 0.0, 0.3, 0.0), true)
] ]
itms[1].color = [1.0, 0.0, 0.2]
// Create a height map based on OpenSimplex noise // Create a height map based on OpenSimplex noise
let hmap = new SimplexHeightMap(1, 1, 256, 50) let hmap = new SimplexHeightMap(1, 1, 256, 50)
@ -106,6 +115,7 @@ async function pipeline () {
game.addRenderFunction(function (gl) { game.addRenderFunction(function (gl) {
gui.draw(gl, itms) gui.draw(gl, itms)
fnt.draw(gl, itms)
}) })
game.startGameLoop() game.startGameLoop()