|
1 | 1 | import * as events from './events' |
| 2 | +import { exportResources } from './exporter/resourcePackExporter' |
2 | 3 | import { GUIStructure } from './guiStructure' |
3 | | -import { safeFunctionName } from './minecraft/util' |
4 | 4 | import { projectSettingStructure } from './projectSettings' |
5 | 5 | import { IRenderedAnimation, renderAllAnimations } from './rendering/animationRenderer' |
6 | | -import { CustomModelData, IRenderedRig, renderRig } from './rendering/modelRenderer' |
| 6 | +import { IRenderedRig, renderRig } from './rendering/modelRenderer' |
7 | 7 | import { animatedJavaSettings, IInfoPopup, Setting as AJSetting, Setting } from './settings' |
8 | 8 | import { openAjFailedProjectExportReadinessDialog } from './ui/popups/failedProjectExportReadiness' |
9 | 9 | import { openUnexpectedErrorDialog } from './ui/popups/unexpectedError' |
10 | 10 | import { consoleGroupCollapsed } from './util/console' |
11 | 11 | import { ExpectedError } from './util/misc' |
12 | 12 | import { NamespacedString } from './util/moddingTools' |
13 | | -import { ProgressBarController } from './util/progress' |
14 | 13 | import { translate } from './util/translation' |
15 | | -import { VirtualFolder } from './util/virtualFileSystem' |
16 | 14 |
|
17 | 15 | type ProjectSettings = Record<NamespacedString, AJSetting<any>> |
18 | | -type NotUndefined<T> = T extends undefined ? never : T |
19 | 16 |
|
20 | 17 | interface IAnimatedJavaExporterOptions<S extends ProjectSettings> { |
21 | 18 | id: NamespacedString |
@@ -151,227 +148,6 @@ export const exportProject = consoleGroupCollapsed('exportProject', async () => |
151 | 148 | Blockbench.showQuickMessage(translate('animated_java.quickmessage.exported_successfully'), 2000) |
152 | 149 | }) |
153 | 150 |
|
154 | | -function showPredicateFileOverwriteConfirmation(path: string) { |
155 | | - const result = confirm( |
156 | | - translate('animated_java.popup.confirm_predicate_file_overwrite.body', { |
157 | | - file: PathModule.parse(path).base, |
158 | | - path, |
159 | | - }), |
160 | | - translate('animated_java.popup.confirm_predicate_file_overwrite.title') |
161 | | - ) |
162 | | - if (!result) throw new ExpectedError('User cancelled export due to predicate file overwrite.') |
163 | | -} |
164 | | - |
165 | | -async function exportResources( |
166 | | - ajSettings: typeof animatedJavaSettings, |
167 | | - projectSettings: NotUndefined<ModelProject['animated_java_settings']>, |
168 | | - rig: IRenderedRig, |
169 | | - rigExportFolder: string, |
170 | | - textureExportFolder: string, |
171 | | - rigItemModelExportPath: string |
172 | | -) { |
173 | | - const projectNamespace = projectSettings.project_namespace.value |
174 | | - const resourcePackPath = PathModule.parse(projectSettings.resource_pack_mcmeta.value).dir |
175 | | - const assetsPackFolder = new VirtualFolder('assets') |
176 | | - const advancedResourcePackSettingsEnabled = |
177 | | - projectSettings.enable_advanced_resource_pack_settings.value |
178 | | - |
179 | | - //------------------------------------ |
180 | | - // Minecraft namespace |
181 | | - //------------------------------------ |
182 | | - |
183 | | - const [rigItemNamespace, rigItemName] = projectSettings.rig_item.value.split(':') |
184 | | - |
185 | | - const minecraftFolder = assetsPackFolder.newFolder('minecraft').newFolder('models/item') |
186 | | - |
187 | | - //------------------------------------ |
188 | | - // Rig Item Predicate File |
189 | | - //------------------------------------ |
190 | | - |
191 | | - interface IPredicateItemModel { |
192 | | - parent: string |
193 | | - textures: any |
194 | | - overrides: Array<{ |
195 | | - predicate: { custom_model_data: number } |
196 | | - model: string |
197 | | - }> |
198 | | - animated_java: { |
199 | | - rigs: Record<string, { used_ids: number[] }> |
200 | | - } |
201 | | - } |
202 | | - |
203 | | - const predicateItemFilePath = PathModule.join( |
204 | | - resourcePackPath, |
205 | | - minecraftFolder.path, |
206 | | - `${rigItemName}.json` |
207 | | - ) |
208 | | - const content: IPredicateItemModel = { |
209 | | - parent: 'item/generated', |
210 | | - textures: { |
211 | | - layer0: `${rigItemNamespace}:item/${rigItemName}`, |
212 | | - }, |
213 | | - overrides: [], |
214 | | - animated_java: { |
215 | | - rigs: {}, |
216 | | - }, |
217 | | - } |
218 | | - const predicateItemFile = minecraftFolder.newFile(`${rigItemName}.json`, content) |
219 | | - let successfullyReadPredicateItemFile = false |
220 | | - if (fs.existsSync(predicateItemFilePath)) { |
221 | | - const stringContent = await fs.promises.readFile(predicateItemFilePath, 'utf8') |
222 | | - try { |
223 | | - const localContent = JSON.parse(stringContent) |
224 | | - Object.assign(content, localContent) |
225 | | - successfullyReadPredicateItemFile = true |
226 | | - } catch (e) { |
227 | | - console.warn('Failed to read predicate item file as JSON') |
228 | | - console.warn(e) |
229 | | - } |
230 | | - } else successfullyReadPredicateItemFile = true |
231 | | - if (!successfullyReadPredicateItemFile || !content.animated_java) { |
232 | | - showPredicateFileOverwriteConfirmation(predicateItemFilePath) |
233 | | - } |
234 | | - if (!content.overrides) content.overrides = [] |
235 | | - if (!content.animated_java.rigs) content.animated_java.rigs = {} |
236 | | - |
237 | | - // const content = predicateItemFile.content as IPredicateItemModel |
238 | | - const usedIds: number[] = [] // IDs that are used by other projects |
239 | | - const consumedIds: number[] = [] // IDs that are used by this project |
240 | | - for (const [name, rig] of Object.entries(content.animated_java.rigs)) { |
241 | | - if (!rig.used_ids) { |
242 | | - console.warn('Found existing rig in predicate file, but it is missing used_ids.') |
243 | | - continue |
244 | | - } |
245 | | - const localUsedIds = rig.used_ids |
246 | | - if (name === projectNamespace) { |
247 | | - // Clean out old overrides |
248 | | - content.overrides = content.overrides.filter(o => { |
249 | | - return !localUsedIds.includes(o.predicate.custom_model_data) |
250 | | - }) |
251 | | - continue |
252 | | - } |
253 | | - usedIds.push(...rig.used_ids) |
254 | | - } |
255 | | - |
256 | | - CustomModelData.usedIds = usedIds |
257 | | - content.animated_java.rigs[projectNamespace] = { |
258 | | - used_ids: consumedIds, |
259 | | - } |
260 | | - |
261 | | - //------------------------------------ |
262 | | - // Project namespace |
263 | | - //------------------------------------ |
264 | | - |
265 | | - const NAMESPACE = projectSettings.project_namespace.value |
266 | | - const namespaceFolder = assetsPackFolder.newFolder(`${NAMESPACE}_animated_java_rig`) |
267 | | - const [modelsFolder, texturesFolder] = namespaceFolder.newFolders( |
268 | | - 'models/item', |
269 | | - 'textures/item' |
270 | | - ) |
271 | | - |
272 | | - for (const texture of Object.values(rig.textures)) { |
273 | | - let image: Buffer |
274 | | - let mcmeta: Buffer | undefined |
275 | | - let optifineEmissive: Buffer | undefined |
276 | | - if (texture.source?.startsWith('data:')) { |
277 | | - image = Buffer.from(texture.source.split(',')[1], 'base64') |
278 | | - } else if (texture.path) { |
279 | | - image = await fs.promises.readFile(texture.path) |
280 | | - if (fs.existsSync(texture.path + '.mcmeta')) |
281 | | - mcmeta = await fs.promises.readFile(texture.path + '.mcmeta') |
282 | | - const emissivePath = texture.path.replace('.png', '') + '_e.png' |
283 | | - if (fs.existsSync(emissivePath)) |
284 | | - optifineEmissive = await fs.promises.readFile(emissivePath) |
285 | | - } else { |
286 | | - throw new Error(`Texture "${texture.name}" has no source or path`) |
287 | | - } |
288 | | - const textureName = safeFunctionName(texture.name) |
289 | | - texturesFolder.newFile(`${textureName}.png`, image) |
290 | | - if (mcmeta) texturesFolder.newFile(`${textureName}.png.mcmeta`, mcmeta) |
291 | | - if (optifineEmissive) texturesFolder.newFile(`${textureName}_e.png`, optifineEmissive) |
292 | | - // console.log(`Exported texture ${texture.name} to ${texturesFolder.path}`) |
293 | | - } |
294 | | - |
295 | | - for (const bone of Object.values(rig.nodeMap)) { |
296 | | - if (bone.type !== 'bone') continue |
297 | | - modelsFolder.newFile(`${bone.name}.json`, bone.model) |
298 | | - consumedIds.push((bone.customModelData = CustomModelData.get())) |
299 | | - predicateItemFile.content.overrides.push({ |
300 | | - predicate: { |
301 | | - custom_model_data: bone.customModelData, |
302 | | - }, |
303 | | - model: bone.resourceLocation, |
304 | | - }) |
305 | | - } |
306 | | - |
307 | | - for (const [variantName, variantBoneMap] of Object.entries(rig.variantModels)) { |
308 | | - if (variantBoneMap.default) continue |
309 | | - const variantFolder = modelsFolder.newFolder(variantName) |
310 | | - for (const [uuid, variantBone] of Object.entries(variantBoneMap)) { |
311 | | - const bone = rig.nodeMap[uuid] |
312 | | - if (bone.type !== 'bone') continue |
313 | | - variantFolder.newFile(`${bone.name}.json`, variantBone.model) |
314 | | - consumedIds.push((variantBone.customModelData = CustomModelData.get())) |
315 | | - predicateItemFile.content.overrides.push({ |
316 | | - predicate: { |
317 | | - custom_model_data: variantBone.customModelData, |
318 | | - }, |
319 | | - model: variantBone.resourceLocation, |
320 | | - }) |
321 | | - } |
322 | | - } |
323 | | - |
324 | | - predicateItemFile.content.overrides.sort( |
325 | | - (a: any, b: any) => a.predicate.custom_model_data - b.predicate.custom_model_data |
326 | | - ) |
327 | | - |
328 | | - if (advancedResourcePackSettingsEnabled) { |
329 | | - const progress = new ProgressBarController( |
330 | | - 'Writing Resource Pack to Disk', |
331 | | - modelsFolder.childCount + texturesFolder.childCount + 1 |
332 | | - ) |
333 | | - await fs.promises.mkdir(rigExportFolder, { recursive: true }) |
334 | | - await modelsFolder.writeChildrenToDisk(rigExportFolder, progress) |
335 | | - |
336 | | - await fs.promises.mkdir(textureExportFolder, { recursive: true }) |
337 | | - await texturesFolder.writeChildrenToDisk(textureExportFolder, progress) |
338 | | - |
339 | | - const predicateItemExportFolder = PathModule.parse(rigItemModelExportPath).dir |
340 | | - await fs.promises.mkdir(predicateItemExportFolder, { recursive: true }) |
341 | | - await predicateItemFile.writeToDisk(predicateItemExportFolder, progress) |
342 | | - |
343 | | - progress.finish() |
344 | | - } else { |
345 | | - const progress = new ProgressBarController( |
346 | | - 'Writing Resource Pack to Disk', |
347 | | - assetsPackFolder.childCount |
348 | | - ) |
349 | | - |
350 | | - const rigFolderPath = PathModule.join(resourcePackPath, namespaceFolder.path) |
351 | | - await fs.promises |
352 | | - .access(rigFolderPath) |
353 | | - .then(async () => { |
354 | | - await fs.promises.rm(rigFolderPath, { recursive: true }) |
355 | | - }) |
356 | | - .catch(e => { |
357 | | - console.warn(e) |
358 | | - }) |
359 | | - |
360 | | - const textureFolderPath = PathModule.join(resourcePackPath, texturesFolder.path) |
361 | | - await fs.promises |
362 | | - .access(textureFolderPath) |
363 | | - .then(async () => { |
364 | | - await fs.promises.rm(textureFolderPath, { recursive: true }) |
365 | | - }) |
366 | | - .catch(e => { |
367 | | - console.warn(e) |
368 | | - }) |
369 | | - |
370 | | - await assetsPackFolder.writeToDisk(resourcePackPath, progress) |
371 | | - progress.finish() |
372 | | - } |
373 | | -} |
374 | | - |
375 | 151 | function verifySettings(structure: GUIStructure, settings: Array<Setting<any>>) { |
376 | 152 | const issues: IInfoPopup[] = [] |
377 | 153 | for (const el of structure) { |
|
0 commit comments