From fdc2ab9421c6a38c6bbd9b621c4da4f2c147a773 Mon Sep 17 00:00:00 2001 From: Ilion Beyst Date: Sun, 6 Nov 2022 17:43:00 +0100 Subject: msdf font renderen --- web/pw-visualizer/assets/res/fonts/roboto.json | 1 + web/pw-visualizer/assets/res/fonts/roboto.png | Bin 0 -> 54869 bytes web/pw-visualizer/assets/shaders/frag/msdf.glsl | 29 ++++ web/pw-visualizer/src/assets.ts | 5 + web/pw-visualizer/src/index.ts | 37 +++-- web/pw-visualizer/src/webgl/msdf_text.ts | 181 ++++++++++++++++++++++++ web/pw-visualizer/src/webgl/texture.ts | 10 +- web/pw-visualizer/src/webgl/util.ts | 1 + 8 files changed, 253 insertions(+), 11 deletions(-) create mode 100644 web/pw-visualizer/assets/res/fonts/roboto.json create mode 100644 web/pw-visualizer/assets/res/fonts/roboto.png create mode 100644 web/pw-visualizer/assets/shaders/frag/msdf.glsl create mode 100644 web/pw-visualizer/src/webgl/msdf_text.ts (limited to 'web/pw-visualizer') diff --git a/web/pw-visualizer/assets/res/fonts/roboto.json b/web/pw-visualizer/assets/res/fonts/roboto.json new file mode 100644 index 0000000..c84b33d --- /dev/null +++ b/web/pw-visualizer/assets/res/fonts/roboto.json @@ -0,0 +1 @@ +{"atlas":{"type":"msdf","distanceRange":8,"size":24,"width":232,"height":232,"yOrigin":"top"},"metrics":{"emSize":1,"lineHeight":1.171875,"ascender":-0.927734375,"descender":0.244140625,"underlineY":0.09765625,"underlineThickness":0.048828125},"glyphs":[{"unicode":32,"advance":0.2490234375},{"unicode":33,"advance":0.26806640625,"planeBounds":{"left":-0.11254882812500004,"top":-0.89371744791666652,"right":0.38745117187499989,"bottom":0.18961588541666666},"atlasBounds":{"left":78.5,"top":34.5,"right":90.5,"bottom":60.5}},{"unicode":34,"advance":0.32421875,"planeBounds":{"left":-0.12613932291666669,"top":-0.934814453125,"right":0.45719401041666663,"bottom":-0.309814453125},"atlasBounds":{"left":172.5,"top":195.5,"right":186.5,"bottom":210.5}},{"unicode":35,"advance":0.6103515625,"planeBounds":{"left":-0.13850911458333331,"top":-0.89713541666666652,"right":0.77815755208333337,"bottom":0.18619791666666666},"atlasBounds":{"left":91.5,"top":34.5,"right":113.5,"bottom":60.5}},{"unicode":36,"advance":0.568359375,"planeBounds":{"left":-0.13273111979166666,"top":-1.0071614583333335,"right":0.70060221354166663,"bottom":0.28450520833333331},"atlasBounds":{"left":60.5,"top":0.5,"right":80.5,"bottom":31.5}},{"unicode":37,"advance":0.734375,"planeBounds":{"left":-0.12963867187499997,"top":-0.89713541666666652,"right":0.870361328125,"bottom":0.18619791666666666},"atlasBounds":{"left":147.5,"top":34.5,"right":171.5,"bottom":60.5}},{"unicode":38,"advance":0.63916015625,"planeBounds":{"left":-0.14152018229166666,"top":-0.89713541666666652,"right":0.81681315104166663,"bottom":0.18619791666666666},"atlasBounds":{"left":21.5,"top":62.5,"right":44.5,"bottom":88.5}},{"unicode":39,"advance":0.1689453125,"planeBounds":{"left":-0.14371744791666669,"top":-0.9365234375,"right":0.31461588541666663,"bottom":-0.3115234375},"atlasBounds":{"left":160.5,"top":195.5,"right":171.5,"bottom":210.5}},{"unicode":40,"advance":0.34912109375,"planeBounds":{"left":-0.11621093750000004,"top":-0.97265625,"right":0.50878906249999989,"bottom":0.40234375},"atlasBounds":{"left":0.5,"top":0.5,"right":15.5,"bottom":33.5}},{"unicode":41,"advance":0.35302734375,"planeBounds":{"left":-0.15771484375000003,"top":-0.97265625,"right":0.46728515624999989,"bottom":0.40234375},"atlasBounds":{"left":16.5,"top":0.5,"right":31.5,"bottom":33.5}},{"unicode":42,"advance":0.4423828125,"planeBounds":{"left":-0.17293294270833331,"top":-0.89558919270833337,"right":0.61873372395833337,"bottom":-0.10392252604166667},"atlasBounds":{"left":87.5,"top":195.5,"right":106.5,"bottom":214.5}},{"unicode":43,"advance":0.5576171875,"planeBounds":{"left":-0.13932291666666666,"top":-0.767578125,"right":0.69401041666666663,"bottom":0.10742187499999999},"atlasBounds":{"left":47.5,"top":195.5,"right":67.5,"bottom":216.5}},{"unicode":44,"advance":0.22021484375,"planeBounds":{"left":-0.15771484375000003,"top":-0.28979492187499994,"right":0.34228515624999989,"bottom":0.335205078125},"atlasBounds":{"left":147.5,"top":195.5,"right":159.5,"bottom":210.5}},{"unicode":45,"advance":0.32861328125,"planeBounds":{"left":-0.14965820312500003,"top":-0.53165690104166663,"right":0.47534179687499989,"bottom":-0.073323567708333301},"atlasBounds":{"left":216.5,"top":170.5,"right":231.5,"bottom":181.5}},{"unicode":46,"advance":0.279296875,"planeBounds":{"left":-0.11450195312500004,"top":-0.30981445312499989,"right":0.38549804687499989,"bottom":0.19018554687500003},"atlasBounds":{"left":38.5,"top":217.5,"right":50.5,"bottom":229.5}},{"unicode":47,"advance":0.39599609375,"planeBounds":{"left":-0.16642252604166666,"top":-0.887451171875,"right":0.54191080729166663,"bottom":0.23754882812499997},"atlasBounds":{"left":194.5,"top":0.5,"right":211.5,"bottom":27.5}},{"unicode":48,"advance":0.568359375,"planeBounds":{"left":-0.13273111979166666,"top":-0.89713541666666652,"right":0.70060221354166663,"bottom":0.18619791666666666},"atlasBounds":{"left":66.5,"top":62.5,"right":86.5,"bottom":88.5}},{"unicode":49,"advance":0.568359375,"planeBounds":{"left":-0.10481770833333336,"top":-0.89835611979166652,"right":0.56184895833333326,"bottom":0.18497721354166666},"atlasBounds":{"left":108.5,"top":62.5,"right":124.5,"bottom":88.5}},{"unicode":50,"advance":0.568359375,"planeBounds":{"left":-0.13102213541666666,"top":-0.90201822916666652,"right":0.70231119791666663,"bottom":0.18131510416666666},"atlasBounds":{"left":125.5,"top":62.5,"right":145.5,"bottom":88.5}},{"unicode":51,"advance":0.568359375,"planeBounds":{"left":-0.14176432291666666,"top":-0.89713541666666652,"right":0.69156901041666663,"bottom":0.18619791666666666},"atlasBounds":{"left":146.5,"top":62.5,"right":166.5,"bottom":88.5}},{"unicode":52,"advance":0.568359375,"planeBounds":{"left":-0.15283203124999997,"top":-0.89713541666666652,"right":0.72216796875,"bottom":0.18619791666666666},"atlasBounds":{"left":167.5,"top":62.5,"right":188.5,"bottom":88.5}},{"unicode":53,"advance":0.568359375,"planeBounds":{"left":-0.12101236979166667,"top":-0.89225260416666652,"right":0.71232096354166663,"bottom":0.19108072916666666},"atlasBounds":{"left":189.5,"top":62.5,"right":209.5,"bottom":88.5}},{"unicode":54,"advance":0.568359375,"planeBounds":{"left":-0.12443033854166667,"top":-0.89396158854166652,"right":0.70890299479166663,"bottom":0.18937174479166666},"atlasBounds":{"left":91.5,"top":116.5,"right":111.5,"bottom":142.5}},{"unicode":55,"advance":0.568359375,"planeBounds":{"left":-0.13614908854166666,"top":-0.89713541666666652,"right":0.69718424479166663,"bottom":0.18619791666666666},"atlasBounds":{"left":211.5,"top":116.5,"right":231.5,"bottom":142.5}},{"unicode":56,"advance":0.568359375,"planeBounds":{"left":-0.13297526041666666,"top":-0.89713541666666652,"right":0.70035807291666663,"bottom":0.18619791666666666},"atlasBounds":{"left":134.5,"top":143.5,"right":154.5,"bottom":169.5}},{"unicode":57,"advance":0.568359375,"planeBounds":{"left":-0.13907877604166666,"top":-0.90030924479166652,"right":0.69425455729166663,"bottom":0.18302408854166666},"atlasBounds":{"left":175.5,"top":143.5,"right":195.5,"bottom":169.5}},{"unicode":58,"advance":0.26513671875,"planeBounds":{"left":-0.11743164062500004,"top":-0.72542317708333337,"right":0.38256835937499989,"bottom":0.19124348958333331},"atlasBounds":{"left":99.5,"top":170.5,"right":111.5,"bottom":192.5}},{"unicode":59,"advance":0.23779296875,"planeBounds":{"left":-0.16365559895833334,"top":-0.71077473958333337,"right":0.37801106770833331,"bottom":0.33089192708333331},"atlasBounds":{"left":196.5,"top":143.5,"right":209.5,"bottom":168.5}},{"unicode":60,"advance":0.50830078125,"planeBounds":{"left":-0.13964843749999997,"top":-0.70515950520833337,"right":0.6103515625,"bottom":0.086507161458333329},"atlasBounds":{"left":68.5,"top":195.5,"right":86.5,"bottom":214.5}},{"unicode":61,"advance":0.5595703125,"planeBounds":{"left":-0.11433919270833333,"top":-0.65999348958333326,"right":0.67732747395833337,"bottom":0.0066731770833333599},"atlasBounds":{"left":127.5,"top":195.5,"right":146.5,"bottom":211.5}},{"unicode":62,"advance":0.5205078125,"planeBounds":{"left":-0.12215169270833333,"top":-0.70515950520833337,"right":0.66951497395833337,"bottom":0.086507161458333329},"atlasBounds":{"left":107.5,"top":195.5,"right":126.5,"bottom":214.5}},{"unicode":63,"advance":0.48681640625,"planeBounds":{"left":-0.15657552083333331,"top":-0.89908854166666652,"right":0.63509114583333337,"bottom":0.18424479166666666},"atlasBounds":{"left":155.5,"top":143.5,"right":174.5,"bottom":169.5}},{"unicode":64,"advance":0.89501953125,"planeBounds":{"left":-0.1348371154108888,"top":-0.88313802083333337,"right":1.0318295512557778,"bottom":0.40852864583333331},"atlasBounds":{"left":98.5,"top":0.5,"right":126.5,"bottom":31.5}},{"unicode":65,"advance":0.66552734375,"planeBounds":{"left":-0.16674804687499997,"top":-0.89713541666666652,"right":0.833251953125,"bottom":0.18619791666666666},"atlasBounds":{"left":109.5,"top":143.5,"right":133.5,"bottom":169.5}},{"unicode":66,"advance":0.63134765625,"planeBounds":{"left":-0.11132812499999999,"top":-0.89713541666666652,"right":0.763671875,"bottom":0.18619791666666666},"atlasBounds":{"left":210.5,"top":62.5,"right":231.5,"bottom":88.5}},{"unicode":67,"advance":0.65283203125,"planeBounds":{"left":-0.12581380208333331,"top":-0.89713541666666652,"right":0.79085286458333337,"bottom":0.18619791666666666},"atlasBounds":{"left":66.5,"top":143.5,"right":88.5,"bottom":169.5}},{"unicode":68,"advance":0.6533203125,"planeBounds":{"left":-0.099853515625,"top":-0.89713541666666652,"right":0.77514648437499989,"bottom":0.18619791666666666},"atlasBounds":{"left":44.5,"top":143.5,"right":65.5,"bottom":169.5}},{"unicode":69,"advance":0.5654296875,"planeBounds":{"left":-0.11149088541666667,"top":-0.89713541666666652,"right":0.72184244791666663,"bottom":0.18619791666666666},"atlasBounds":{"left":23.5,"top":143.5,"right":43.5,"bottom":169.5}},{"unicode":70,"advance":0.5498046875,"planeBounds":{"left":-0.097493489583333329,"top":-0.89713541666666652,"right":0.69417317708333337,"bottom":0.18619791666666666},"atlasBounds":{"left":89.5,"top":143.5,"right":108.5,"bottom":169.5}},{"unicode":71,"advance":0.68115234375,"planeBounds":{"left":-0.12386067708333333,"top":-0.89713541666666652,"right":0.79280598958333337,"bottom":0.18619791666666666},"atlasBounds":{"left":0.5,"top":143.5,"right":22.5,"bottom":169.5}},{"unicode":72,"advance":0.7109375,"planeBounds":{"left":-0.10335286458333333,"top":-0.89713541666666652,"right":0.81331380208333337,"bottom":0.18619791666666666},"atlasBounds":{"left":188.5,"top":116.5,"right":210.5,"bottom":142.5}},{"unicode":73,"advance":0.2822265625,"planeBounds":{"left":-0.087565104166666699,"top":-0.89713541666666652,"right":0.37076822916666663,"bottom":0.18619791666666666},"atlasBounds":{"left":176.5,"top":116.5,"right":187.5,"bottom":142.5}},{"unicode":74,"advance":0.55517578125,"planeBounds":{"left":-0.16227213541666666,"top":-0.89225260416666652,"right":0.67106119791666663,"bottom":0.19108072916666666},"atlasBounds":{"left":155.5,"top":116.5,"right":175.5,"bottom":142.5}},{"unicode":75,"advance":0.63037109375,"planeBounds":{"left":-0.10359700520833333,"top":-0.89713541666666652,"right":0.81306966145833337,"bottom":0.18619791666666666},"atlasBounds":{"left":132.5,"top":116.5,"right":154.5,"bottom":142.5}},{"unicode":76,"advance":0.54150390625,"planeBounds":{"left":-0.099934895833333329,"top":-0.89713541666666652,"right":0.69173177083333337,"bottom":0.18619791666666666},"atlasBounds":{"left":112.5,"top":116.5,"right":131.5,"bottom":142.5}},{"unicode":77,"advance":0.87548828125,"planeBounds":{"left":-0.10416666666666666,"top":-0.89713541666666652,"right":0.97916666666666652,"bottom":0.18619791666666666},"atlasBounds":{"left":205.5,"top":34.5,"right":231.5,"bottom":60.5}},{"unicode":78,"advance":0.71044921875,"planeBounds":{"left":-0.10359700520833333,"top":-0.89713541666666652,"right":0.81306966145833337,"bottom":0.18619791666666666},"atlasBounds":{"left":68.5,"top":116.5,"right":90.5,"bottom":142.5}},{"unicode":79,"advance":0.6904296875,"planeBounds":{"left":-0.13419596354166666,"top":-0.89713541666666652,"right":0.82413736979166663,"bottom":0.18619791666666666},"atlasBounds":{"left":44.5,"top":116.5,"right":67.5,"bottom":142.5}},{"unicode":80,"advance":0.6396484375,"planeBounds":{"left":-0.099365234375,"top":-0.89713541666666652,"right":0.77563476562499989,"bottom":0.18619791666666666},"atlasBounds":{"left":22.5,"top":116.5,"right":43.5,"bottom":142.5}},{"unicode":81,"advance":0.6904296875,"planeBounds":{"left":-0.13663736979166666,"top":-0.90299479166666652,"right":0.82169596354166663,"bottom":0.30533854166666663},"atlasBounds":{"left":159.5,"top":0.5,"right":182.5,"bottom":29.5}},{"unicode":82,"advance":0.62451171875,"planeBounds":{"left":-0.096923828125,"top":-0.89713541666666652,"right":0.77807617187499989,"bottom":0.18619791666666666},"atlasBounds":{"left":0.5,"top":116.5,"right":21.5,"bottom":142.5}},{"unicode":83,"advance":0.60400390625,"planeBounds":{"left":-0.13574218749999997,"top":-0.89713541666666652,"right":0.7392578125,"bottom":0.18619791666666666},"atlasBounds":{"left":191.5,"top":89.5,"right":212.5,"bottom":115.5}},{"unicode":84,"advance":0.607421875,"planeBounds":{"left":-0.15437825520833331,"top":-0.89713541666666652,"right":0.76228841145833337,"bottom":0.18619791666666666},"atlasBounds":{"left":168.5,"top":89.5,"right":190.5,"bottom":115.5}},{"unicode":85,"advance":0.65234375,"planeBounds":{"left":-0.11035156249999999,"top":-0.89225260416666652,"right":0.7646484375,"bottom":0.19108072916666666},"atlasBounds":{"left":146.5,"top":89.5,"right":167.5,"bottom":115.5}},{"unicode":86,"advance":0.6474609375,"planeBounds":{"left":-0.17602539062499997,"top":-0.89713541666666652,"right":0.823974609375,"bottom":0.18619791666666666},"atlasBounds":{"left":121.5,"top":89.5,"right":145.5,"bottom":115.5}},{"unicode":87,"advance":0.88037109375,"planeBounds":{"left":-0.16129557291666657,"top":-0.89713541666666652,"right":1.0470377604166665,"bottom":0.18619791666666666},"atlasBounds":{"left":91.5,"top":89.5,"right":120.5,"bottom":115.5}},{"unicode":88,"advance":0.6328125,"planeBounds":{"left":-0.16202799479166666,"top":-0.89713541666666652,"right":0.79630533854166663,"bottom":0.18619791666666666},"atlasBounds":{"left":67.5,"top":89.5,"right":90.5,"bottom":115.5}},{"unicode":89,"advance":0.609375,"planeBounds":{"left":-0.17496744791666666,"top":-0.89713541666666652,"right":0.78336588541666663,"bottom":0.18619791666666666},"atlasBounds":{"left":43.5,"top":89.5,"right":66.5,"bottom":115.5}},{"unicode":90,"advance":0.6025390625,"planeBounds":{"left":-0.13378906249999997,"top":-0.89713541666666652,"right":0.7412109375,"bottom":0.18619791666666666},"atlasBounds":{"left":21.5,"top":89.5,"right":42.5,"bottom":115.5}},{"unicode":91,"advance":0.2744140625,"planeBounds":{"left":-0.10652669270833336,"top":-0.99723307291666652,"right":0.43513997395833331,"bottom":0.33610026041666663},"atlasBounds":{"left":32.5,"top":0.5,"right":45.5,"bottom":32.5}},{"unicode":92,"advance":0.41796875,"planeBounds":{"left":-0.15820312499999997,"top":-0.887451171875,"right":0.591796875,"bottom":0.23754882812499997},"atlasBounds":{"left":0.5,"top":34.5,"right":18.5,"bottom":61.5}},{"unicode":93,"advance":0.2744140625,"planeBounds":{"left":-0.16463216145833334,"top":-0.99723307291666652,"right":0.37703450520833331,"bottom":0.33610026041666663},"atlasBounds":{"left":46.5,"top":0.5,"right":59.5,"bottom":32.5}},{"unicode":94,"advance":0.427734375,"planeBounds":{"left":-0.16137695312499997,"top":-0.88761393229166663,"right":0.588623046875,"bottom":-0.17928059895833334},"atlasBounds":{"left":213.5,"top":89.5,"right":231.5,"bottom":106.5}},{"unicode":95,"advance":0.451171875,"planeBounds":{"left":-0.17024739583333331,"top":-0.18229166666666663,"right":0.62141927083333337,"bottom":0.27604166666666669},"atlasBounds":{"left":51.5,"top":217.5,"right":70.5,"bottom":228.5}},{"unicode":96,"advance":0.322265625,"planeBounds":{"left":-0.15226236979166669,"top":-0.92626953125,"right":0.43107096354166663,"bottom":-0.42626953125},"atlasBounds":{"left":23.5,"top":217.5,"right":37.5,"bottom":229.5}},{"unicode":97,"advance":0.54150390625,"planeBounds":{"left":-0.12459309895833333,"top":-0.72249348958333337,"right":0.66707356770833337,"bottom":0.19417317708333331},"atlasBounds":{"left":112.5,"top":170.5,"right":131.5,"bottom":192.5}},{"unicode":98,"advance":0.56298828125,"planeBounds":{"left":-0.12394205729166667,"top":-0.9326171875,"right":0.70939127604166663,"bottom":0.19238281249999997},"atlasBounds":{"left":19.5,"top":34.5,"right":39.5,"bottom":61.5}},{"unicode":99,"advance":0.52392578125,"planeBounds":{"left":-0.12898763020833331,"top":-0.72249348958333337,"right":0.66267903645833337,"bottom":0.19417317708333331},"atlasBounds":{"left":59.5,"top":170.5,"right":78.5,"bottom":192.5}},{"unicode":100,"advance":0.56494140625,"planeBounds":{"left":-0.14615885416666666,"top":-0.9326171875,"right":0.68717447916666663,"bottom":0.19238281249999997},"atlasBounds":{"left":40.5,"top":34.5,"right":60.5,"bottom":61.5}},{"unicode":101,"advance":0.53662109375,"planeBounds":{"left":-0.14322916666666666,"top":-0.72249348958333337,"right":0.69010416666666663,"bottom":0.19417317708333331},"atlasBounds":{"left":17.5,"top":170.5,"right":37.5,"bottom":192.5}},{"unicode":102,"advance":0.3544921875,"planeBounds":{"left":-0.14485677083333334,"top":-0.942626953125,"right":0.52180989583333326,"bottom":0.18237304687499997},"atlasBounds":{"left":61.5,"top":34.5,"right":77.5,"bottom":61.5}},{"unicode":103,"advance":0.56689453125,"planeBounds":{"left":-0.14347330729166666,"top":-0.70670572916666652,"right":0.68986002604166663,"bottom":0.37662760416666663},"atlasBounds":{"left":0.5,"top":62.5,"right":20.5,"bottom":88.5}},{"unicode":104,"advance":0.55517578125,"planeBounds":{"left":-0.11775716145833333,"top":-0.91666666666666652,"right":0.67390950520833337,"bottom":0.16666666666666666},"atlasBounds":{"left":185.5,"top":34.5,"right":204.5,"bottom":60.5}},{"unicode":105,"advance":0.255859375,"planeBounds":{"left":-0.12158203125000004,"top":-0.90616861979166652,"right":0.37841796874999989,"bottom":0.17716471354166666},"atlasBounds":{"left":172.5,"top":34.5,"right":184.5,"bottom":60.5}},{"unicode":106,"advance":0.25048828125,"planeBounds":{"left":-0.21500651041666669,"top":-0.90364583333333337,"right":0.36832682291666663,"bottom":0.38802083333333331},"atlasBounds":{"left":144.5,"top":0.5,"right":158.5,"bottom":31.5}},{"unicode":107,"advance":0.52197265625,"planeBounds":{"left":-0.12272135416666667,"top":-0.91666666666666652,"right":0.71061197916666663,"bottom":0.16666666666666666},"atlasBounds":{"left":126.5,"top":34.5,"right":146.5,"bottom":60.5}},{"unicode":108,"advance":0.255859375,"planeBounds":{"left":-0.1014811197916667,"top":-0.91666666666666652,"right":0.35685221354166663,"bottom":0.16666666666666666},"atlasBounds":{"left":114.5,"top":34.5,"right":125.5,"bottom":60.5}},{"unicode":109,"advance":0.87060546875,"planeBounds":{"left":-0.10636393229166667,"top":-0.70654296875,"right":0.97696940104166652,"bottom":0.16845703124999997},"atlasBounds":{"left":132.5,"top":170.5,"right":158.5,"bottom":191.5}},{"unicode":110,"advance":0.55615234375,"planeBounds":{"left":-0.11775716145833333,"top":-0.70654296875,"right":0.67390950520833337,"bottom":0.16845703124999997},"atlasBounds":{"left":27.5,"top":195.5,"right":46.5,"bottom":216.5}},{"unicode":111,"advance":0.5693359375,"planeBounds":{"left":-0.13199869791666666,"top":-0.72249348958333337,"right":0.70133463541666663,"bottom":0.19417317708333331},"atlasBounds":{"left":38.5,"top":170.5,"right":58.5,"bottom":192.5}},{"unicode":112,"advance":0.56298828125,"planeBounds":{"left":-0.12418619791666667,"top":-0.70914713541666652,"right":0.70914713541666663,"bottom":0.37418619791666663},"atlasBounds":{"left":87.5,"top":62.5,"right":107.5,"bottom":88.5}},{"unicode":113,"advance":0.56787109375,"planeBounds":{"left":-0.14640299479166666,"top":-0.70914713541666652,"right":0.68693033854166663,"bottom":0.37418619791666663},"atlasBounds":{"left":0.5,"top":89.5,"right":20.5,"bottom":115.5}},{"unicode":114,"advance":0.3515625,"planeBounds":{"left":-0.11279296875000004,"top":-0.70654296875,"right":0.51220703124999989,"bottom":0.16845703124999997},"atlasBounds":{"left":179.5,"top":170.5,"right":194.5,"bottom":191.5}},{"unicode":115,"advance":0.5166015625,"planeBounds":{"left":-0.14046223958333331,"top":-0.72249348958333337,"right":0.65120442708333337,"bottom":0.19417317708333331},"atlasBounds":{"left":79.5,"top":170.5,"right":98.5,"bottom":192.5}},{"unicode":116,"advance":0.3330078125,"planeBounds":{"left":-0.17830403645833334,"top":-0.823486328125,"right":0.48836263020833331,"bottom":0.17651367187499997},"atlasBounds":{"left":0.5,"top":170.5,"right":16.5,"bottom":194.5}},{"unicode":117,"advance":0.5556640625,"planeBounds":{"left":-0.11897786458333333,"top":-0.69677734375,"right":0.67268880208333337,"bottom":0.17822265624999997},"atlasBounds":{"left":159.5,"top":170.5,"right":178.5,"bottom":191.5}},{"unicode":118,"advance":0.4951171875,"planeBounds":{"left":-0.17008463541666666,"top":-0.70166015625,"right":0.66324869791666663,"bottom":0.17333984374999997},"atlasBounds":{"left":210.5,"top":143.5,"right":230.5,"bottom":164.5}},{"unicode":119,"advance":0.7431640625,"planeBounds":{"left":-0.17081705729166666,"top":-0.70166015625,"right":0.91251627604166652,"bottom":0.17333984374999997},"atlasBounds":{"left":0.5,"top":195.5,"right":26.5,"bottom":216.5}},{"unicode":120,"advance":0.50341796875,"planeBounds":{"left":-0.16471354166666666,"top":-0.70166015625,"right":0.66861979166666663,"bottom":0.17333984374999997},"atlasBounds":{"left":195.5,"top":170.5,"right":215.5,"bottom":191.5}},{"unicode":121,"advance":0.48681640625,"planeBounds":{"left":-0.17374674479166666,"top":-0.69913736979166652,"right":0.65958658854166663,"bottom":0.38419596354166663},"atlasBounds":{"left":45.5,"top":62.5,"right":65.5,"bottom":88.5}},{"unicode":122,"advance":0.50341796875,"planeBounds":{"left":-0.14143880208333331,"top":-0.70166015625,"right":0.65022786458333337,"bottom":0.17333984374999997},"atlasBounds":{"left":212.5,"top":0.5,"right":231.5,"bottom":21.5}},{"unicode":123,"advance":0.33544921875,"planeBounds":{"left":-0.15901692708333334,"top":-0.94783528645833337,"right":0.50764973958333326,"bottom":0.34383138020833331},"atlasBounds":{"left":127.5,"top":0.5,"right":143.5,"bottom":31.5}},{"unicode":124,"advance":0.2509765625,"planeBounds":{"left":-0.08235677083333337,"top":-0.89371744791666652,"right":0.33430989583333331,"bottom":0.31461588541666663},"atlasBounds":{"left":183.5,"top":0.5,"right":193.5,"bottom":29.5}},{"unicode":125,"advance":0.33544921875,"planeBounds":{"left":-0.17268880208333334,"top":-0.94783528645833337,"right":0.49397786458333331,"bottom":0.34383138020833331},"atlasBounds":{"left":81.5,"top":0.5,"right":97.5,"bottom":31.5}},{"unicode":126,"advance":0.66455078125,"planeBounds":{"left":-0.12581380208333331,"top":-0.58561197916666663,"right":0.79085286458333337,"bottom":-0.0022786458333333044},"atlasBounds":{"left":0.5,"top":217.5,"right":22.5,"bottom":231.5}}],"kerning":[]} diff --git a/web/pw-visualizer/assets/res/fonts/roboto.png b/web/pw-visualizer/assets/res/fonts/roboto.png new file mode 100644 index 0000000..3723e70 Binary files /dev/null and b/web/pw-visualizer/assets/res/fonts/roboto.png differ diff --git a/web/pw-visualizer/assets/shaders/frag/msdf.glsl b/web/pw-visualizer/assets/shaders/frag/msdf.glsl new file mode 100644 index 0000000..5d2325b --- /dev/null +++ b/web/pw-visualizer/assets/shaders/frag/msdf.glsl @@ -0,0 +1,29 @@ +#extension GL_OES_standard_derivatives : enable +#ifdef GL_ES +precision mediump float; +#endif + +varying vec2 v_texCoord; +uniform sampler2D msdf; +uniform vec4 u_bgColor; +uniform vec4 u_fgColor; + +uniform float u_distanceRange; +uniform float u_glyphSize; +uniform vec2 u_resolution; +uniform vec4 u_viewbox; + +float median(float r, float g, float b) { + return max(min(r, g), min(max(r, g), b)); +} + +void main() { + float scale = u_distanceRange / u_glyphSize * u_resolution.y / u_viewbox.w; + + vec3 msd = texture2D(msdf, v_texCoord).rgb; + float sd = median(msd.r, msd.g, msd.b); + float screenPxRange = max(u_distanceRange, scale); + float screenPxDistance = screenPxRange*(sd - 0.5); + float opacity = clamp(screenPxDistance + 0.5, 0.0, 1.0); + gl_FragColor = vec4(u_fgColor.rgb, u_fgColor.a * opacity); +} \ No newline at end of file diff --git a/web/pw-visualizer/src/assets.ts b/web/pw-visualizer/src/assets.ts index 5fa5215..abd17e4 100644 --- a/web/pw-visualizer/src/assets.ts +++ b/web/pw-visualizer/src/assets.ts @@ -9,11 +9,16 @@ export {default as shipPng} from "../assets/res/ship.png"; export {default as fontPng} from "../assets/res/font.png"; +export {default as robotoMsdfPng} from "../assets/res/fonts/roboto.png"; +export {default as robotoMsdfJson} from "../assets/res/fonts/roboto.json"; + export {default as imageFragmentShader} from "../assets/shaders/frag/image.glsl?url"; export {default as maskedImageFragmentShader} from "../assets/shaders/frag/masked_image.glsl?url"; export {default as simpleFragmentShader} from "../assets/shaders/frag/simple.glsl?url"; export {default as vorFragmentShader} from "../assets/shaders/frag/vor.glsl?url"; +export {default as msdfFragmentShader} from "../assets/shaders/frag/msdf.glsl?url"; + export {default as imageVertexShader} from "../assets/shaders/vert/image.glsl?url"; export {default as simpleVertexShader} from "../assets/shaders/vert/simple.glsl?url"; diff --git a/web/pw-visualizer/src/index.ts b/web/pw-visualizer/src/index.ts index 4231707..97bdb3d 100644 --- a/web/pw-visualizer/src/index.ts +++ b/web/pw-visualizer/src/index.ts @@ -25,6 +25,7 @@ import { defaultLabelFactory, LabelFactory, Align, Label } from "./webgl/text"; import { VoronoiBuilder } from "./voronoi/voronoi"; import * as assets from "./assets"; import { loadImage, Texture } from "./webgl/texture"; +import { defaultMsdfLabelFactory, MsdfLabelFactory, Label as MsdfLabel } from "./webgl/msdf_text"; function to_bbox(box: number[]): BBox { @@ -84,7 +85,7 @@ export function init() { ms_per_frame = parseInt(ELEMENTS["speed"].value); - GL = CANVAS.getContext("webgl"); + GL = CANVAS.getContext("webgl", { antialias: true }); GL.clearColor(0, 0, 0, 1); GL.clear(GL.COLOR_BUFFER_BIT); @@ -124,9 +125,12 @@ export class GameInstance { image_shader: Shader; masked_image_shader: Shader; + msdf_shader: Shader; + text_factory: LabelFactory; - planet_labels: Label[]; - ship_labels: Label[]; + msdf_text_factory: MsdfLabelFactory; + planet_labels: MsdfLabel[]; + ship_labels: MsdfLabel[]; ship_ibo: IndexBuffer; ship_vao: VertexArray; @@ -153,6 +157,7 @@ export class GameInstance { planets_textures: Texture[], ship_texture: Texture, font_texture: Texture, + robotoMsdfTexture: Texture, shaders: Dictionary ) { this.game = game; @@ -168,7 +173,9 @@ export class GameInstance { }); this.masked_image_shader = shaders["masked_image"].create_shader(GL); + this.msdf_shader = shaders["msdf"].create_shader(GL); this.text_factory = defaultLabelFactory(GL, font_texture, this.image_shader); + this.msdf_text_factory = defaultMsdfLabelFactory(GL, robotoMsdfTexture, this.msdf_shader); this.planet_labels = []; this.ship_labels = []; @@ -278,11 +285,11 @@ export class GameInstance { 1, 0, -planets[i * 3], - -planets[i * 3 + 1] - 1.2, + -planets[i * 3 + 1] - 1.171875, 1, ]); - const label = this.text_factory.build(GL, transform); + const label = this.msdf_text_factory.build(GL, transform); this.planet_labels.push(label); this.renderer.addRenderable(label.getRenderable(), LAYERS.planet_label); } @@ -330,7 +337,7 @@ export class GameInstance { this.planet_labels[i].setText( GL, - "*" + planet_ships[i], + "" + planet_ships[i], Align.Middle, Align.Begin ); @@ -375,7 +382,7 @@ export class GameInstance { const renderable = new DefaultRenderable(ib, vao, this.masked_image_shader, [this.ship_texture], {}); this.renderer.addRenderable(renderable, LAYERS.ship); - const label = this.text_factory.build(GL); + const label = this.msdf_text_factory.build(GL); this.ship_labels.push(label); this.renderer.addRenderable(label.getRenderable(), LAYERS.ship_label); @@ -451,10 +458,11 @@ export class GameInstance { this.shader, this.image_shader, this.masked_image_shader, + this.msdf_shader, ]; - // If not playing, still reder with different viewbox, so people can still pan etc. + // If not playing, still render with different viewbox, so that panning is still possible if (!this.playing) { this.last_time = time; @@ -595,6 +603,7 @@ export async function set_instance(source: string): Promise { loadImage(assets.fontPng), loadImage(assets.shipPng), loadImage(assets.earthPng), + loadImage(assets.robotoMsdfPng), ]; const shader_promies = [ @@ -630,7 +639,14 @@ export async function set_instance(source: string): Promise { assets.simpleVertexShader, ), ])(), - + (async () => + <[string, ShaderFactory]>[ + "msdf", + await ShaderFactory.create_factory( + assets.msdfFragmentShader, + assets.simpleVertexShader, + ), + ])(), ]; let shaders_array: [string, ShaderFactory][]; [texture_images, shaders_array] = await Promise.all([ @@ -646,12 +662,15 @@ export async function set_instance(source: string): Promise { const fontTexture = Texture.fromImage(GL, texture_images[0], "font"); const shipTexture = Texture.fromImage(GL, texture_images[1], "ship"); const earthTexture = Texture.fromImage(GL, texture_images[2], "earth"); + const robotoMsdfTexture = Texture.fromImage(GL, texture_images[3], "robotoMsdf"); + game_instance = new GameInstance( Game.new(source), [earthTexture], shipTexture, fontTexture, + robotoMsdfTexture, shaders ); diff --git a/web/pw-visualizer/src/webgl/msdf_text.ts b/web/pw-visualizer/src/webgl/msdf_text.ts new file mode 100644 index 0000000..5bbac20 --- /dev/null +++ b/web/pw-visualizer/src/webgl/msdf_text.ts @@ -0,0 +1,181 @@ +import { Shader, Uniform1f, Uniform4f, UniformMatrix3fv } from "./shader"; +import { Texture } from "./texture"; +import { DefaultRenderable } from "./renderer"; +import { IndexBuffer, VertexBuffer } from "./buffer"; +import { VertexBufferLayout, VertexArray } from "./vertexBufferLayout"; +import { robotoMsdfJson } from "../assets"; +import { GlypInfo } from "./text"; + + +export enum Align { + Begin, + End, + Middle, +} + +export type FontAtlas = { + atlas: AtlasMeta, + metrics: Metrics, + glyphs: Glyph[], +} + +export type AtlasMeta = { + type: string, + distanceRange: number, + size: number, + width: number, + height: number, + yOrigin: string, +} + +export type Metrics = { + emSize: number, + lineHeight: number, + ascender: number, + descender: number, + underlineY: number, + underlineThickness: number, +} + + +export type Glyph = { + unicode: number, + advance: number, + planeBounds?: Bounds, + atlasBounds?: Bounds, +} + +export type Bounds = { + left: number, + bottom: number, + right: number, + top: number, +} + + +export class MsdfLabelFactory { + texture: Texture; + font: FontAtlas; + shader: Shader; + + constructor(gl: WebGLRenderingContext, fontTexture: Texture, font: FontAtlas, shader: Shader) { + this.texture = fontTexture; + this.font = font; + this.shader = shader; + } + + build(gl: WebGLRenderingContext, transform?: UniformMatrix3fv): Label { + return new Label(gl, this.shader, this.texture, this.font, transform); + } +} + +export class Label { + inner: DefaultRenderable; + + font: FontAtlas; + charAtlas: {[unicodeNumber: number]: Glyph}; + + constructor(gl: WebGLRenderingContext, shader: Shader, tex: Texture, font: FontAtlas, transform: UniformMatrix3fv) { + this.font = font; + this.charAtlas = {} + this.font.glyphs.forEach((glyph) => { + this.charAtlas[glyph.unicode] = glyph; + }); + + const uniforms = { + "u_trans": transform, + "u_trans_next": transform, + "u_fgColor": new Uniform4f([1.0, 1.0, 1.0, 1.0]), + "u_bgColor": new Uniform4f([0.0, 0.0, 0.0, 1.0]), + "u_distanceRange": new Uniform1f(font.atlas.distanceRange), + "u_glyphSize": new Uniform1f(font.atlas.size), + }; + const ib = new IndexBuffer(gl, []); + const vb_pos = new VertexBuffer(gl, []); + const vb_tex = new VertexBuffer(gl, []); + + const layout_pos = new VertexBufferLayout(); + layout_pos.push(gl.FLOAT, 2, 4, "a_position"); + + const layout_tex = new VertexBufferLayout(); + layout_tex.push(gl.FLOAT, 2, 4, "a_texCoord"); + + const vao = new VertexArray(); + vao.addBuffer(vb_pos, layout_pos); + vao.addBuffer(vb_tex, layout_tex); + + this.inner = new DefaultRenderable(ib, vao, shader, [tex], uniforms); + } + + getRenderable(): DefaultRenderable { + return this.inner; + } + + setText(gl: WebGLRenderingContext, text: string, h_align = Align.Begin, v_align = Align.Begin) { + const idxs = []; + const verts_pos = []; + const verts_tex = []; + + let xPos = 0; + let yPos = 0; + switch (v_align) { + case Align.Begin: + yPos = -1; + break; + case Align.End: + yPos = 0; + break; + case Align.Middle: + yPos = -0.5; + break; + } + + // track position in the index buffer + let bufPos = 0; + for (let charIndex = 0; charIndex < text.length; charIndex++) { + let char = this.charAtlas[text.charCodeAt(charIndex)] + if (char.atlasBounds && char.planeBounds) { + verts_pos.push(xPos + char.planeBounds.left, yPos-char.planeBounds.top); + verts_pos.push(xPos + char.planeBounds.right, yPos-char.planeBounds.top); + verts_pos.push(xPos + char.planeBounds.left, yPos-char.planeBounds.bottom); + verts_pos.push(xPos + char.planeBounds.right, yPos-char.planeBounds.bottom); + + const atlasWidth = this.font.atlas.width; + const atlasHeight = this.font.atlas.height; + + verts_tex.push(char.atlasBounds.left / atlasWidth, char.atlasBounds.top / atlasHeight); + verts_tex.push(char.atlasBounds.right / atlasWidth, char.atlasBounds.top / atlasHeight); + verts_tex.push(char.atlasBounds.left / atlasWidth, char.atlasBounds.bottom / atlasHeight); + verts_tex.push(char.atlasBounds.right / atlasWidth, char.atlasBounds.bottom / atlasHeight); + + idxs.push(bufPos+0, bufPos+1, bufPos+2); + idxs.push(bufPos+1, bufPos+2, bufPos+3); + bufPos += 4; + } + xPos += char.advance; + } + + let shift = 0; + switch (h_align) { + case Align.End: + shift = xPos; + break; + case Align.Middle: + shift = xPos / 2; + break; + } + + for (let i = 0; i < verts_pos.length; i += 2) { + verts_pos[i] -= shift; + } + + + this.inner.updateIndexBuffer(gl, idxs); + this.inner.updateVAOBuffer(gl, 0, verts_pos); + this.inner.updateVAOBuffer(gl, 1, verts_tex); + } +} + +export function defaultMsdfLabelFactory(gl: WebGLRenderingContext, fontTexture: Texture, shader: Shader): MsdfLabelFactory { + return new MsdfLabelFactory(gl, fontTexture, robotoMsdfJson, shader); +} diff --git a/web/pw-visualizer/src/webgl/texture.ts b/web/pw-visualizer/src/webgl/texture.ts index 9624489..9ebd5c0 100644 --- a/web/pw-visualizer/src/webgl/texture.ts +++ b/web/pw-visualizer/src/webgl/texture.ts @@ -67,8 +67,14 @@ export class Texture { gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + if (name == "font") { + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + } else { + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + + } gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([255, 0, 0, 255])); diff --git a/web/pw-visualizer/src/webgl/util.ts b/web/pw-visualizer/src/webgl/util.ts index 7b55d19..922b016 100644 --- a/web/pw-visualizer/src/webgl/util.ts +++ b/web/pw-visualizer/src/webgl/util.ts @@ -74,6 +74,7 @@ export class Resizer { mouse_pos = [0, 0]; last_drag = [0, 0]; + // x, y, w, h viewbox: number[]; orig_viewbox: number[]; -- cgit v1.2.3