var osfs = '/'
var selected = [];
var prev_side = 'north';
var uv_clipboard;
var outliner, texturelist;
var pe_list_data = []
var open_dialog = false;
var open_interface = false;
var tex_version = 1;
var pe_list;
const Pressing = {
	shift: false,
	ctrl: false,
	alt: false,
}
var main_uv;
var Prop = {
	active_panel	: 'preview',
	wireframe	  	: false,
	file_path	  	: '',
	file_name	  	: '',
	added_models 	: 0,
	project_saved 	: true,
	fps				: 0,
	zoom			: 100,
	progress		: 0,
	session 		: false,
	connections 	: 0,
	facing		 	: 'north'
}
const Project = {
	name			: '',
	parent			: '',
	geometry_name	: '',
	description	   	: '',
	_box_uv 		: false,
	get box_uv() {return Project._box_uv},
	set box_uv(v) {
		if (Project._box_uv != v) {
			Project._box_uv = v;
			switchAutoUV(v);
		}
	},
	texture_width	: 16,
	texture_height	: 16,
	ambientocclusion: true,
	get optional_box_uv() {
		return Format.optional_box_uv;
	}
}
const mouse_pos = {x:0,y:0}
const sort_collator = new Intl.Collator(undefined, {numeric: true, sensitivity: 'base'});

$.ajaxSetup({ cache: false });


function initializeApp() {
	//Browser Detection
	Blockbench.browser = 'electron'
	if (isApp === false) {
		if (navigator.userAgent.toLowerCase().indexOf('firefox') > -1) {
			Blockbench.browser = 'firefox'
		} else if (!!window.chrome && !!window.chrome.webstore) {
			Blockbench.browser = 'chrome'
		} else if ((!!window.opr && !!opr.addons) || !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0) {
			Blockbench.browser = 'opera'
		} else if (/constructor/i.test(window.HTMLElement) || (function (p) { return p.toString() === "[object SafariRemoteNotification]"; })(!window['safari'] || (typeof safari !== 'undefined' && safari.pushNotification))) {
			Blockbench.browser = 'safari'
		} else if (!!document.documentMode) {
			Blockbench.browser = 'internet_explorer'
		} else if (!!window.StyleMedia) {
			Blockbench.browser = 'edge'
		}
		if (navigator.appVersion.indexOf("Win") != -1) 	OSName = 'Windows';
		if (navigator.appVersion.indexOf("Mac") != -1) 	OSName = 'MacOS';
		if (navigator.appVersion.indexOf("Linux") != -1)OSName = 'Linux';
		if (['edge', 'internet_explorer'].includes(Blockbench.browser)) {
			alert(capitalizeFirstLetter(Blockbench.browser)+' does not support Blockbench')
		}
		$('.local_only').remove()
	} else {
		$('.web_only').remove()
	}
	if (localStorage.getItem('welcomed_version') != appVersion) {
		Blockbench.addFlag('after_update')
		localStorage.setItem('welcomed_version', appVersion)
	}
	BARS.setupActions()
	BARS.setupToolbars()
	BARS.setupVue()
	MenuBar.setup()

	//Misc
	translateUI()
	console.log('Blockbench ' + appVersion + (isApp
		? (' Desktop (' + Blockbench.operating_system +')')
		: (' Web ('+capitalizeFirstLetter(Blockbench.browser)+')')
	))
	var startups = parseInt(localStorage.getItem('startups')||0);
	localStorage.setItem('startups', startups+1);


	if (isApp) {
		updateRecentProjects()
	}
	
	setInterval(function() {
		Prop.fps = framespersecond;
		framespersecond = 0;
	}, 1000)

	main_uv = new UVEditor('main_uv', false, true)
	main_uv.setToMainSlot()

	onVueSetup.funcs.forEach((func) => {
		if (typeof func === 'function') {
			func()
		}
	})

//JQuery UI
	$('#cubes_list').droppable({
		greedy: true,
		accept: 'div.outliner_object',
		tolerance: 'pointer',
		hoverClass: 'drag_hover',
		drop: function(event, ui) {
			var item = Outliner.root.findRecursive('uuid', $(ui.draggable).parent().attr('id'))
			dropOutlinerObjects(item, undefined, event)
		}
	})
	$('#cubes_list').contextmenu(function(event) {
		event.stopPropagation();
		event.preventDefault();
		Interface.Panels.outliner.menu.show(event)
	})
	$('#texture_list').contextmenu(function(event) {
		Interface.Panels.textures.menu.show(event)
	})

	setupInterface()
	setupDragHandlers()
	Modes.options.start.select()
	Blockbench.setup_successful = true

}
function onVueSetup(func) {
	if (!onVueSetup.funcs) {
		onVueSetup.funcs = []
	}
	onVueSetup.funcs.push(func)
}
function canvasGridSize(shift, ctrl) {
	if (!shift && !ctrl) {
		return 16 / limitNumber(settings.edit_size.value, 1, 512)
	} else if (ctrl && shift) {
		var basic = 16 / limitNumber(settings.edit_size.value, 1, 512)
		var control = 16 / limitNumber(settings.ctrl_size.value, 1, 4096)
		var shift = 16 / limitNumber(settings.shift_size.value, 1, 4096)
		control = basic / control
		return shift / control
	} else if (ctrl) {
		return 16 / limitNumber(settings.ctrl_size.value, 1, 4096)
	} else {
		return 16 / limitNumber(settings.shift_size.value, 1, 4096)
	}
}
function updateNslideValues() {

	if (selected.length) {
		BarItems.slider_pos_x.update()
		BarItems.slider_pos_y.update()
		BarItems.slider_pos_z.update()

		BarItems.slider_size_x.update()
		BarItems.slider_size_y.update()
		BarItems.slider_size_z.update()

		BarItems.slider_inflate.update()
	}
	if (selected.length || (Format.bone_rig && Group.selected)) {
		BarItems.slider_origin_x.update()
		BarItems.slider_origin_y.update()
		BarItems.slider_origin_z.update()

		BarItems.slider_rotation_x.update()
		BarItems.slider_rotation_y.update()
		BarItems.slider_rotation_z.update()
		if (Format.bone_rig) {
			BarItems.bone_reset_toggle.setIcon(Group.selected && Group.selected.reset ? 'check_box' : 'check_box_outline_blank')
		} else {
			BarItems.rescale_toggle.setIcon(selected[0].rescale ? 'check_box' : 'check_box_outline_blank')
		}
	}
}

//Selections
function updateSelection() {
	elements.forEach(obj => {
		if (selected.includes(obj) && !obj.selected) {
			obj.selectLow()
		} else if (!selected.includes(obj) && obj.selected) {
			obj.unselect()
		}
	})
	Cube.all.forEach(cube => {
		if (cube.visibility) {
			var mesh = cube.mesh
			if (mesh && mesh.outline) {
				mesh.outline.visible = cube.selected
			}
		}
	})
	for (var i = Cube.selected.length-1; i >= 0; i--) {
		if (!selected.includes(Cube.selected[i])) {
			Cube.selected.splice(i, 1)
		}
	}
	if (Cube.selected.length) {
		main_uv.jquery.size.find('.uv_mapping_overlay').remove()
		main_uv.loadData()
		$('.selection_only').css('visibility', 'visible')
	} else {
		if (Format.bone_rig && Group.selected) {
			$('.selection_only').css('visibility', 'hidden')
			$('.selection_only#element').css('visibility', 'visible')
		} else {
			$('.selection_only').css('visibility', 'hidden')
			if (Locator.selected.length) {
				$('.selection_only#element').css('visibility', 'visible')
			}
		}
	}
	if (Modes.animate) {
		if (Animator.selected && Group.selected) {
			Animator.selected.getBoneAnimator().select()
		}
		updateKeyframeSelection()
	}

	BarItems.cube_counter.update()
	updateNslideValues()
	Blockbench.globalMovement = isMovementGlobal()

	Canvas.updateOrigin()
	Transformer.updateSelection()
	Transformer.update()

	BARS.updateConditions()
	delete TickUpdates.selection;
	Blockbench.dispatchEvent('update_selection')
}
function selectAll() {
	if (selected.length < elements.length) {
		elements.forEach(obj => {
			obj.selectLow()
		})
		updateSelection()
	} else {
		unselectAll()
	}
	Blockbench.dispatchEvent('select_all')
}
function unselectAll() {
	selected.forEachReverse(obj => obj.unselect())
	if (Group.selected) Group.selected.unselect()
	Group.all.forEach(function(s) {
		s.selected = false
	})
	updateSelection()
}
function createSelection() {
	if ($('#selgen_new').is(':checked')) {
		selected.length = 0
	}
	if (Group.selected) {
		Group.selected.unselect()
	}
	var name_seg = $('#selgen_name').val().toUpperCase()
	var rdm = $('#selgen_random').val()/100

	var array = elements
	if ($('#selgen_group').is(':checked') && Group.selected) {
		array = Group.selected.children
	}

	array.forEach(function(s) {
		if (s.name.toUpperCase().includes(name_seg) === false) return;
		if (Math.random() > rdm) return;
		selected.push(s)
	})
	updateSelection()
	if (selected.length) {
		selected[0].showInOutliner()
	}
	hideDialog()
}
//Modes
class Mode extends KeybindItem {
	constructor(data) {
		super(data)
		this.id = data.id;
		this.name = data.name || tl('mode.'+this.id);
		this.selected = false
		this.default_tool = data.default_tool;
		this.center_windows = data.center_windows||['preview'];
		this.selectCubes = data.selectCubes !== false
		this.hide_toolbars = data.hide_toolbars
		this.condition = data.condition;
		this.onSelect = data.onSelect;
		this.onUnselect = data.onUnselect;
		Modes.options[this.id] = this;
	}
	select() {
		var scope = this;
		if (Modes.selected) {
			delete Modes[Modes.selected.id];
		}
		if (typeof Modes.selected.onUnselect === 'function') {
			Modes.selected.onUnselect()
		}
		if (Modes.selected.selected) {
			Modes.selected.selected = false
		}
		this.selected = true;
		Modes.id = this.id
		Modes.selected = this;
		Modes[Modes.selected.id] = true;

		$('#center > #preview').toggle(this.center_windows.includes('preview'));
		$('#center > #timeline').toggle(this.center_windows.includes('timeline'));
		$('#center > #start_screen').toggle(this.center_windows.includes('start_screen'));

		if (this.hide_toolbars) {
			$('#main_toolbar .toolbar_wrapper').css('visibility', 'hidden')
		} else {
			$('#main_toolbar .toolbar_wrapper').css('visibility', 'visible')
		}

		if (typeof this.onSelect === 'function') {
			this.onSelect()
		}

		updateInterface()
		Canvas.updateRenderSides()
		if (BarItems[this.default_tool]) {
			BarItems[this.default_tool].select()
		} else {
			BarItems.move_tool.select()
		}
		updateSelection()
	}
	trigger() {
		if (Condition(this.condition)) {
			this.select()
		}
	}
}
const Modes = {
	id: 'edit',
	selected: false,
	options: {},
};
onVueSetup(function() {
	Modes.vue = new Vue({
		el: '#mode_selector',
		data: {
			options: Modes.options
		}
	})
});
BARS.defineActions(function() {
	new Mode({
		id: 'start',
		category: 'navigate',
		center_windows: ['start_screen'],
		hide_toolbars: true,
		onSelect: function () {
		},
		onUnselect: function () {
		}
	})
	new Mode({
		id: 'edit',
		default_tool: 'move_tool',
		category: 'navigate',
		condition: () => Format,
		keybind: new Keybind({key: 49})
	})
	new Mode({
		id: 'paint',
		default_tool: 'brush_tool',
		category: 'navigate',
		condition: () => Format,
		keybind: new Keybind({key: 50}),
		onSelect: () => {
			Cube.all.forEach(cube => {
				Canvas.buildGridBox(cube)
			})
		},
		onUnselect: () => {
			Cube.all.forEach(cube => {
				Canvas.buildGridBox(cube)
			})
		},
	})
	new Mode({
		id: 'display',
		selectCubes: false,
		default_tool: 'move_tool',
		category: 'navigate',
		keybind: new Keybind({key: 51}),
		condition: () => Format.display_mode,
		onSelect: () => {
			enterDisplaySettings()
		},
		onUnselect: () => {
			exitDisplaySettings()
		},
	})
	new Mode({
		id: 'animate',
		default_tool: 'move_tool',
		category: 'navigate',
		center_windows: ['preview', 'timeline'],
		keybind: new Keybind({key: 51}),
		condition: () => Format.animation_mode,
		onSelect: () => {
			Animator.join()
		},
		onUnselect: () => {
			Animator.leave()
		}
	})
})
//Backup
setInterval(function() {
	if (Outliner.root.length || textures.length) {
		try {
			var model = Codecs.project.compile();
			localStorage.setItem('backup_model', model)
		} catch (err) {
			console.log('Unable to create backup. ', err)
		}
	}
}, 1e3*30)
//Misc
const TickUpdates = {
	Run() {
		if (TickUpdates.outliner) {
			delete TickUpdates.outliner;
			loadOutlinerDraggable()
		}
		if (TickUpdates.selection) {
			delete TickUpdates.selection;
			updateSelection()
		}
		if (TickUpdates.main_uv) {
			delete TickUpdates.main_uv;
			main_uv.loadData()
		}
		if (TickUpdates.texture_list) {
			delete TickUpdates.texture_list;
			loadTextureDraggable();
		}
		if (TickUpdates.keyframes) {
			delete TickUpdates.keyframes;
			Vue.nextTick(Timeline.update)
		}
	}
}
const Screencam = {
	fullScreen(options, cb) {
		setTimeout(function() {
			currentwindow.capturePage(function(screenshot) {
				var dataUrl = screenshot.toDataURL()
				dataUrl = dataUrl.replace('data:image/png;base64,','')
				Jimp.read(Buffer.from(dataUrl, 'base64')).then(function(image) { 

					if (options && options.width && options.height) {
						image.contain(options.width, options.height)
					}

					image.getBase64(Jimp.MIME_PNG, function(a, dataUrl){
						Screencam.returnScreenshot(dataUrl, cb)
					})
				});
			})
		}, 40)
	},
	returnScreenshot(dataUrl, cb) {
		if (cb) {
			cb(dataUrl)
		} else if (isApp) {
			var screenshot = nativeImage.createFromDataURL(dataUrl)
			var img = new Image()
			var is_gif = dataUrl.substr(5, 9) == 'image/gif'
			img.src = dataUrl

			var btns = [tl('dialog.cancel'), tl('dialog.save')]
			if (!is_gif) {
				btns.push(tl('message.screenshot.clipboard'))
			}
			Blockbench.showMessageBox({
				translateKey: 'screenshot',
				icon: img,
				buttons: btns,
				confirm: 1,
				cancel: 0
			}, function(result) {
				if (result === 1) {
					electron.dialog.showSaveDialog(currentwindow, {filters: [ {name: tl('data.image'), extensions: [is_gif ? 'gif' : 'png']} ]}, function (fileName) {
						if (fileName === undefined) {
							return;
						}
						fs.writeFile(fileName, Buffer(dataUrl.split(',')[1], 'base64'), err => {})
					})
				} else if (result === 2) {
					clipboard.writeImage(screenshot)
				}
			})
		} else {
			new Dialog({
				title: tl('message.screenshot.right_click'), 
				id: 'screenie', 
				lines: ['<img src="'+dataUrl+'" width="600px" class="allow_default_menu"></img>'],
				draggable: true,
				singleButton: true
			}).show()
		}
	},
	cleanCanvas(options, cb) {
		quad_previews.current.screenshot(options, cb)
	},
	createGif(options, cb) {
		/*
		var images = [];
		var preview = quad_previews.current;
		var interval = setInterval(function() {

			var shot = preview.canvas.toDataURL()
			images.push(shot);

			if (images.length >= options.length/1000*options.fps) {
				clearInterval(interval);
				gifshot.createGIF({
					images,
					frameDuration: 10/options.fps,
					progressCallback: cl,
					text: 'BLOCKBENCH'
				}, obj => {
					Screencam.returnScreenshot(obj.image, cb);
				})
			}
		}, 1000/options.fps)
		//Does not support transparency
		*/
		

		if (typeof options !== 'object') {
			options = {}
		}
		if (!options.length) {
			options.length = 1000
		}
		var preview = quad_previews.current;
		var interval = options.fps ? (1000/options.fps) : 100
		var gif = new GIF({
			repeat: options.repeat,
			quality: options.quality,
			transparent: 0x000000,
		})
		var frame_count = (options.length/interval)

		if (options.turnspeed) {
			preview.controls.autoRotate = true;
			preview.controls.autoRotateSpeed = options.turnspeed;
		}

		gif.on('finished', blob => {
			var reader = new FileReader()
			reader.onload = () => {
				if (!options.silent) {
					Blockbench.setProgress(0)
					Blockbench.setStatusBarText()
				}
				Screencam.returnScreenshot(reader.result, cb)
			}
			reader.readAsDataURL(blob)
		})
		if (!options.silent) {
			Blockbench.setStatusBarText(tl('status_bar.recording_gif'))
			gif.on('progress', Blockbench.setProgress)
		}
		var frames = 0;
		var loop = setInterval(() => {
			var img = new Image()
			img.src = preview.canvas.toDataURL()
			img.onload = () => {
				gif.addFrame(img, {delay: interval})
			}
			Blockbench.setProgress(interval*frames/options.length)
			frames++;
		}, interval)

		setTimeout(() => {
			clearInterval(loop)
			if (!options.silent) {
				Blockbench.setStatusBarText(tl('status_bar.processing_gif'))
			}
			gif.render()
			if (Animator.open && Timeline.playing) {
				Timeline.pause()
			}
			if (options.turnspeed) {
				preview.controls.autoRotate = false;
			}
		}, options.length)
	}
}
const Clipbench = {
	elements: [],
	copy(event, cut) {
		var p = Prop.active_panel
		var text = window.getSelection()+'';
		if (text) {
			Clipbench.setText(text)

		} else if (open_dialog == 'uv_dialog') {
			uv_dialog.copy(event)

		} else if (display_mode) {
			DisplayMode.copy()

		} else if (Animator.open) {
			if (Timeline.selected.length) {
				Clipbench.setKeyframes(Timeline.selected)
			}
		} else if (p == 'uv' || p == 'preview') {
			main_uv.copy(event)
			
		} else if (p == 'textures' && isApp) {
			if (textures.selected) {
				Clipbench.setTexture(textures.selected)
			}
		} else if (p == 'outliner') {
			Clipbench.setElements()
			Clipbench.setGroup()
			if (Group.selected) {
				Clipbench.setGroup(Group.selected)
			} else {
				Clipbench.setElements(selected)
			}
			if (cut) {
				BarItems.delete.trigger()
			}
		}
	},
	paste(event) {
		var p = Prop.active_panel
		if (open_dialog == 'uv_dialog') {
			uv_dialog.paste(event)
		} else if (display_mode) {
			DisplayMode.paste()
		} else if (Animator.open) {
			//
			if (isApp) {
				var raw = clipboard.readHTML()
				try {
					var data = JSON.parse(raw)
					if (data.type === 'keyframes' && data.content) {
						Clipbench.keyframes = data.content
					}
				} catch (err) {}
			}
			if (Clipbench.keyframes && Clipbench.keyframes.length) {

				if (!Animator.selected) return;
				var bone = Animator.selected.getBoneAnimator()
				if (bone) {
					var keyframes = [];
					Undo.initEdit({keyframes, keep_saved: true});
					Clipbench.keyframes.forEach(function(data, i) {
						var base_kf = new Keyframe(data);
						base_kf.time = Timeline.second + data.time_offset;
						bone.pushKeyframe(base_kf);
						keyframes.push(base_kf);
						base_kf.select(i ? {ctrlKey: true} : null)
					})
					Animator.preview()
					Vue.nextTick(Timeline.update);
					Undo.finishEdit('paste keyframes');
				}
			}
		} else if (p == 'uv' || p == 'preview') {
			main_uv.paste(event)
		} else if (p == 'textures' && isApp) {
			var img = clipboard.readImage()
			if (img) {
				var dataUrl = img.toDataURL()
				var texture = new Texture({name: 'pasted', folder: 'block' }).fromDataURL(dataUrl).fillParticle().add(true)
				setTimeout(function() {
					texture.openMenu()
				},40)
			}
		} else if (p == 'outliner') {
			
			Undo.initEdit({outliner: true, elements: [], selection: true});
			//Group
			var target = 'root'
			if (Group.selected) {
				target = Group.selected
				Group.selected.isOpen = true
			} else if (selected[0]) {
				target = selected[0]
			}
			selected.length = 0
			if (isApp) {
				var raw = clipboard.readHTML()
				try {
					var data = JSON.parse(raw)
					if (data.type === 'elements' && data.content) {
						Clipbench.group = undefined;
						Clipbench.elements = data.content;
					} else if (data.type === 'group' && data.content) {
						Clipbench.group = data.content;
						Clipbench.elements = [];
					}
				} catch (err) {}
			}
			if (Clipbench.group) {
				function iterate(obj, parent) {
					if (obj.children) {
						var copy = new Group(obj).addTo(parent).init()
						if (obj.children && obj.children.length) {
							obj.children.forEach((child) => {
								iterate(child, copy)
							})
						}
					} else {
						var el = NonGroup.fromSave(obj).addTo(parent).selectLow();
						Canvas.adaptObjectPosition(el);
					}
				}
				iterate(Clipbench.group, target)
				updateSelection()

			} else if (Clipbench.elements && Clipbench.elements.length) {
				Clipbench.elements.forEach(function(obj) {
					NonGroup.fromSave(obj).addTo(target).selectLow()
				})
				updateSelection()
			}
			Undo.finishEdit('paste', {outliner: true, elements: selected, selection: true});
		}
	},
	setTexture(texture) {
		//Sets the raw image of the texture
		if (!isApp) return;

		if (texture.mode === 'bitmap') {
			var img = nativeImage.createFromDataURL(texture.source)
		} else {
			var img = nativeImage.createFromPath(texture.source.split('?')[0])
		}
		clipboard.writeImage(img)
	},
	setGroup(group) {
		if (!group) {
			Clipbench.group = undefined
			return;
		}
		Clipbench.group = group.getSaveCopy()
		if (isApp) {
			clipboard.writeHTML(JSON.stringify({type: 'group', content: Clipbench.group}))
		}
	},
	setElements(arr) {
		if (!arr) {
			Clipbench.elements = []
			return;
		}
		arr.forEach(function(obj) {
			Clipbench.elements.push(obj.getSaveCopy())
		})
		if (isApp) {
			clipboard.writeHTML(JSON.stringify({type: 'elements', content: Clipbench.elements}))
		}
	},
	setKeyframes(keyframes) {
		Clipbench.keyframes = []
		if (!keyframes || keyframes.length === 0) {
			return;
		}
		var first = keyframes[0];
		keyframes.forEach(function(kf) {
			if (kf.time < first.time) {
				first = kf
			}
		})
		keyframes.forEach(function(kf) {
			Clipbench.keyframes.push({
				channel: kf.channel,
				x: kf.x,
				y: kf.y,
				z: kf.z,
				w: kf.w,
				isQuaternion: kf.isQuaternion,
				time_offset: kf.time - first.time,
			})
		})
		if (isApp) {
			clipboard.writeHTML(JSON.stringify({type: 'keyframes', content: Clipbench.keyframes}))
		}
	},
	setText(text) {
		if (isApp) {
			clipboard.writeText(text)
		} else {
			document.execCommand('copy')
		}
	}
}

const entityMode = {
	old_res: {},
	hardcodes: {"geometry.chicken":{"body":{"rotation":[90,0,0]}},"geometry.llama":{"chest1":{"rotation":[0,90,0]},"chest2":{"rotation":[0,90,0]},"body":{"rotation":[90,0,0]}},"geometry.cow":{"body":{"rotation":[90,0,0]}},"geometry.sheep.sheared":{"body":{"rotation":[90,0,0]}},"geometry.sheep":{"body":{"rotation":[90,0,0]}},"geometry.phantom":{"body":{"rotation":[0,0,0]},"wing0":{"rotation":[0,0,5.7]},"wingtip0":{"rotation":[0,0,5.7]},"wing1":{"rotation":[0,0,-5.7]},"wingtip1":{"rotation":[0,0,-5.7]},"head":{"rotation":[11.5,0,0]},"tail":{"rotation":[0,0,0]},"tailtip":{"rotation":[0,0,0]}},"geometry.pig":{"body":{"rotation":[90,0,0]}},"geometry.ocelot":{"body":{"rotation":[90,0,0]},"tail1":{"rotation":[90,0,0]},"tail2":{"rotation":[90,0,0]}},"geometry.cat":{"body":{"rotation":[90,0,0]},"tail1":{"rotation":[90,0,0]},"tail2":{"rotation":[90,0,0]}},"geometry.turtle":{"eggbelly":{"rotation":[90,0,0]},"body":{"rotation":[90,0,0]}},"geometry.villager.witch":{"hat2":{"rotation":[-3,0,1.5]},"hat3":{"rotation":[-6,0,3]},"hat4":{"rotation":[-12,0,6]}},"geometry.pufferfish.mid":{"spines_top_front":{"rotation":[45,0,0]},"spines_top_back":{"rotation":[-45,0,0]},"spines_bottom_front":{"rotation":[-45,0,0]},"spines_bottom_back":{"rotation":[45,0,0]},"spines_left_front":{"rotation":[0,45,0]},"spines_left_back":{"rotation":[0,-45,0]},"spines_right_front":{"rotation":[0,-45,0]},"spines_right_back":{"rotation":[0,45,0]}},"geometry.pufferfish.large":{"spines_top_front":{"rotation":[45,0,0]},"spines_top_back":{"rotation":[-45,0,0]},"spines_bottom_front":{"rotation":[-45,0,0]},"spines_bottom_back":{"rotation":[45,0,0]},"spines_left_front":{"rotation":[0,45,0]},"spines_left_back":{"rotation":[0,-45,0]},"spines_right_front":{"rotation":[0,-45,0]},"spines_right_back":{"rotation":[0,45,0]}},"geometry.tropicalfish_a":{"leftFin":{"rotation":[0,-35,0]},"rightFin":{"rotation":[0,35,0]}},"geometry.tropicalfish_b":{"leftFin":{"rotation":[0,-35,0]},"rightFin":{"rotation":[0,35,0]}}},
}