Skip to content

Commit

Permalink
[docker-push] Add the capability to localize textures with text
Browse files Browse the repository at this point in the history
  • Loading branch information
marvin-roesch committed Oct 12, 2020
1 parent 7d40697 commit a412eeb
Show file tree
Hide file tree
Showing 56 changed files with 116 additions and 39 deletions.
45 changes: 34 additions & 11 deletions bin/convert-textures.js
Original file line number Diff line number Diff line change
@@ -1,34 +1,57 @@
/* eslint-disable no-console */
const childProcess = require('child_process')
const { lstatSync, readdirSync } = require('fs')
const path = require('path')
const imagemin = require('imagemin')
const imageminWebp = require('imagemin-webp')
const imageminZopfli = require('imagemin-zopfli')

const isDirectory = source => lstatSync(path.resolve(source)).isDirectory()

const textures = {
map_bg: { lossy: true },
map: { hqAvailable: true },
shadesmar_map_bg: { lossy: true },
transition: {},
text_pattern: {},
map_text: { hqAvailable: true },
shadesmar_map_text: { hqAvailable: true },
hover_text: {},
map_text: { hqAvailable: true, localized: true },
shadesmar_map_text: { hqAvailable: true, localized: true },
hover_text: { localized: true },
factions: { hqAvailable: true },
oathgates_silver_kingdoms: { hqAvailable: true }
oathgates_text: { hqAvailable: true, localized: true },
silver_kingdoms: { hqAvailable: true },
silver_kingdoms_text: { hqAvailable: true, localized: true }
}

const basePath = './public/img/textures'
const basePath = './src/assets/textures'
const webpOnly = process.argv.slice(2)[0] === '--webp-only'
const locales = readdirSync(path.resolve(`${basePath}/localized`)).filter(locale => isDirectory(`${basePath}/localized/${locale}`))

Promise.all(Object.keys(textures).flatMap((name) => {
const texture = textures[name]
const transformed = []

const files = [`${basePath}/${name}.png`]
if (texture.hqAvailable === true) {
files.push(`${basePath}/hq_${name}.png`)
if (texture.localized) {
locales.forEach((locale) => {
const files = [`${basePath}/localized/${locale}/${name}.png`]
if (texture.hqAvailable === true) {
files.push(`${basePath}/localized/${locale}/hq_${name}.png`)
}
transformed.push({ ...texture, name: `${name}[${locale}]`, files, destination: `${basePath}/localized/${locale}` })
})
} else {
const files = [`${basePath}/${name}.png`]
if (texture.hqAvailable === true) {
files.push(`${basePath}/hq_${name}.png`)
}
transformed.push({ ...texture, name, files, destination: basePath })
}

const changedFiles = files.filter(path => childProcess.execSync(`git status -s ${path}`).toString().length > 0)
return transformed
}).flatMap((texture) => {
const { name } = texture

const changedFiles = texture.files.filter(path => childProcess.execSync(`git status -s ${path}`).toString().length > 0)

if (changedFiles.length === 0) {
console.log(`Files for texture '${name}' haven't changed, ignoring...`)
Expand All @@ -38,15 +61,15 @@ Promise.all(Object.keys(textures).flatMap((name) => {
console.log(`Optimizing and converting texture '${name}'...`)

return (webpOnly ? new Promise(resolve => resolve()) : imagemin(changedFiles, {
destination: basePath,
destination: texture.destination,
plugins: [
imageminZopfli({ more: true })
]
})).then(() => {
console.log(`Optimized PNGs for texture '${name}'`)

return imagemin(changedFiles, {
destination: basePath,
destination: texture.destination,
plugins: [
imageminWebp({ quality: texture.lossy === true ? 90 : 100, lossless: texture.lossy !== true })
]
Expand Down
Binary file not shown.
Binary file not shown.
Binary file removed public/img/textures/oathgates_silver_kingdoms.png
Binary file not shown.
Binary file removed public/img/textures/oathgates_silver_kingdoms.webp
Binary file not shown.
File renamed without changes
File renamed without changes.
File renamed without changes
File renamed without changes.
File renamed without changes
File renamed without changes.
File renamed without changes.
Binary file added src/assets/textures/hq_silver_kingdoms.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/textures/hq_silver_kingdoms.webp
Binary file not shown.
File renamed without changes
File renamed without changes.
File renamed without changes
File renamed without changes.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
File renamed without changes
File renamed without changes.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
File renamed without changes
File renamed without changes.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
File renamed without changes
File renamed without changes.
File renamed without changes
File renamed without changes.
File renamed without changes
File renamed without changes.
Binary file added src/assets/textures/silver_kingdoms.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/textures/silver_kingdoms.webp
Binary file not shown.
File renamed without changes
File renamed without changes.
File renamed without changes
File renamed without changes.
12 changes: 7 additions & 5 deletions src/components/map/Map.vue
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ export default {
this.renderer.setSize(window.innerWidth, window.innerHeight)
this.renderer.sortObjects = false
this.textureManager = new TextureManager(this.renderer)
this.textureManager = new TextureManager(this.renderer, this.$t('textureLocale'))
this.composer = new EffectComposer(this.renderer)
} catch (error) {
Expand Down Expand Up @@ -131,10 +131,12 @@ export default {
shadesmar_map_bg: {},
transition: {},
text_pattern: {},
map_text: { hqAvailable: true },
shadesmar_map_text: { hqAvailable: true },
map_text: { hqAvailable: true, localized: true },
shadesmar_map_text: { hqAvailable: true, localized: true },
factions: { hqAvailable: true },
oathgates_silver_kingdoms: { hqAvailable: true }
oathgates_text: { hqAvailable: true, localized: true },
silver_kingdoms: { hqAvailable: true },
silver_kingdoms_text: { hqAvailable: true, localized: true }
}
return this.textureManager.load(textures)
Expand Down Expand Up @@ -252,7 +254,7 @@ export default {
this.shatteringPass = new ShatteringPass()
this.composer.addPass(this.shatteringPass)
this.hoverTexture = await this.textureManager.loadData('hover_text', false, 'gb')
this.hoverTexture = await this.textureManager.loadData('hover_text', false, true, 'gb')
},
onEventChanged (event, oldEvent) {
this.highlights.children.forEach(h => h.leave())
Expand Down
46 changes: 31 additions & 15 deletions src/components/map/TextureManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import isMobile from 'is-mobile'
import { TextureLoader } from 'three'

export default class TextureManager {
constructor (renderer) {
constructor (renderer, locale) {
const maxTextureSize = renderer.capabilities.maxTextureSize
this.useHq = maxTextureSize >= 8192 && !isMobile({ tablet: true, featureDetect: true })
this.webpPromise = new Promise((resolve) => {
Expand All @@ -17,45 +17,57 @@ export default class TextureManager {
}
img.src = 'data:image/webp;base64,UklGRjIAAABXRUJQVlA4ICYAAACyAgCdASoCAAEALmk0mk0iIiIiIgBoSygABc6zbAAA/v56QAAAAA=='
})
this.locale = locale
}

buildPath (prefix, name) {
return `${process.env.BASE_URL}img/textures/${prefix}${name}.${this.webpSupported ? 'webp' : 'png'}`
buildPath (prefix, name, localized) {
const base = `${prefix}${name}.${this.webpSupported ? 'webp' : 'png'}`

if (localized === undefined || localized === false) {
return base
}

return `localized/${this.locale}/${base}`
}

load (textures) {
const result = {}

const textureLoader = new TextureLoader()

return this.webpPromise.then(() => new Promise((resolve) => {
return this.webpPromise.then(() => new Promise((resolve, reject) => {
Object.keys(textures).forEach((name) => {
const texture = textures[name]

const prefix = (texture.hqAvailable || (texture.hqWebpAvailable && this.webpSupported)) && this.useHq ? 'hq_' : ''
const path = this.buildPath(prefix, name)
const path = this.buildPath(prefix, name, texture.localized)

textureLoader.load(path, (data) => {
texture.loaded = true
result[name] = data
textureLoader.load(
require(`@/assets/textures/${path}`),
(data) => {
texture.loaded = true
result[name] = data

if (Object.keys(textures).every(t => textures[t].loaded === true)) {
resolve(result)
}
})
if (Object.keys(textures).every(t => textures[t].loaded === true)) {
resolve(result)
}
},
undefined,
errorEvent => reject(new Error(`Could not load texture '${name}': ${errorEvent.message}`))
)
})
}))
}

loadData (name, hqAvailable, channelsToKeep) {
loadData (name, hqAvailable, localized, channelsToKeep) {
const channels = {}
channelsToKeep.split('').forEach((c) => {
channels[c] = channelsToKeep.indexOf(c)
})

const channelNames = Object.keys(channels)

return this.webpPromise.then(() => new Promise((resolve) => {
return this.webpPromise.then(() => new Promise((resolve, reject) => {
const image = new Image()

image.onload = () => {
Expand Down Expand Up @@ -92,7 +104,11 @@ export default class TextureManager {
resolve({ width: raw.width, height: raw.height, data })
}

image.src = this.buildPath(hqAvailable && this.useHq ? 'hq_' : '', name)
image.onerror = errorEvent => reject(new Error(`Could not load texture '${name}': ${errorEvent.message}`))

const path = this.buildPath(hqAvailable && this.useHq ? 'hq_' : '', name, localized)

image.src = require(`@/assets/textures/${path}`)
}))
}
}
2 changes: 1 addition & 1 deletion src/components/map/layers/Oathgates.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export default class Oathgates extends Group {
`,
fragmentShader: oathgateTextFragmentShader,
uniforms: {
Texture: { value: textures.oathgates_silver_kingdoms },
Texture: { value: textures.oathgates_text },
PatternTexture: { value: textures.text_pattern },
Opacity: { value: this.t }
},
Expand Down
4 changes: 2 additions & 2 deletions src/components/map/layers/SilverKingdoms.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export default class SilverKingdoms extends Group {
`,
fragmentShader: silverKingdomsFragmentShader,
uniforms: {
Texture: { value: textures.oathgates_silver_kingdoms },
Texture: { value: textures.silver_kingdoms },
Opacity: { value: this.t }
},
extensions: {
Expand All @@ -56,7 +56,7 @@ export default class SilverKingdoms extends Group {
`,
fragmentShader: silverKingdomsTextFragmentShader,
uniforms: {
Texture: { value: textures.oathgates_silver_kingdoms },
Texture: { value: textures.silver_kingdoms_text },
PatternTexture: { value: textures.text_pattern },
Opacity: { value: this.t }
},
Expand Down
2 changes: 1 addition & 1 deletion src/components/map/layers/silverKingdomsFragmentShader.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,6 @@ export default `
vec4 map = texture2D(Texture, vUv);
gl_FragColor = Sample(map.g, 20., maxGrad);
gl_FragColor = Sample(map.r, 20., maxGrad);
}
`
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,6 @@ export default `
float noise = texture2D(PatternTexture, vUv * vec2(16., 8.)).r;
vec4 map = texture2D(Texture, vUv);
gl_FragColor = Sample(map.b, noise, 24., 60., 10., maxGrad);
gl_FragColor = Sample(map.r, noise, 24., 60., 10., maxGrad);
}
`
1 change: 1 addition & 0 deletions src/i18n.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const loadedLanguages = ['en'] // our default language that is preloaded
function setI18nLanguage (lang) {
i18n.locale = lang
document.querySelector('html').setAttribute('lang', lang)
document.querySelector('title').innerHTML = i18n.t('title')
return lang
}

Expand Down
2 changes: 2 additions & 0 deletions src/lang/en.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
{
"textureLocale": "en",
"title": "Map of Roshar | The Stormlight Archive",
"ui": {
"loading": "Loading...",
"circa": "ca. {date}",
Expand Down
2 changes: 2 additions & 0 deletions src/lang/es.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
{
"textureLocale": "en",
"title": "Map of Roshar | The Stormlight Archive",
"ui": {
"loading": "Loading...",
"circa": "ca. {date}",
Expand Down
2 changes: 2 additions & 0 deletions src/lang/zh.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
{
"textureLocale": "en",
"title": "Map of Roshar | The Stormlight Archive",
"ui": {
"loading": "Loading...",
"circa": "ca. {date}",
Expand Down
13 changes: 11 additions & 2 deletions src/routes.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Vue from 'vue'
import VueRouter from 'vue-router'
import { loadLanguageAsync } from '@/i18n'
import { i18n, loadLanguageAsync } from '@/i18n'
import store from '@/store'

Vue.use(VueRouter)
Expand Down Expand Up @@ -75,7 +75,16 @@ router.beforeEach((to, from, next) => {
return
}

loadLanguage(locale).then(() => next()).catch(() => next('/en-US'))
loadLanguage(locale).catch(() => next('/en-US')).then(() => next())
})

router.afterEach((to, from) => {
const oldLocale = from.params.locale
const newLocale = to.params.locale

if (oldLocale !== undefined && i18n.t('textureLocale', newLocale) !== i18n.t('textureLocale', oldLocale)) {
location.reload()
}
})

export { router }
22 changes: 21 additions & 1 deletion vue.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
module.exports = {
chainWebpack: (config) => {
config.module
.rule('images')
.use('url-loader')
.loader('url-loader')
.tap((options) => {
options.fallback.options.context = './src/assets'
options.fallback.options.name = 'img/[path][name].[hash:8].[ext]'

return options
})
},
pwa: {
name: 'Roshar Map',
themeColor: '#0f3562',
Expand All @@ -9,11 +21,19 @@ module.exports = {
workboxOptions: {
skipWaiting: true,
clientsClaim: true,
exclude: ['index.html'],
exclude: ['index.html', /^\/img\/textures\/localized\/[^/]+\/.*\.(webp|png)/, /^\/js\/lang-.*\.js/],
runtimeCaching: [
{
urlPattern: /^$|^\/$|\/#.*$/,
handler: 'NetworkFirst'
},
{
urlPattern: /^\/?img\/textures\/localized\/[^/]+\/.*\.(webp|png)/,
handler: 'CacheFirst'
},
{
urlPattern: /^\/?js\/lang-.*\.js/,
handler: 'CacheFirst'
}
]
}
Expand Down

0 comments on commit a412eeb

Please sign in to comment.