Merge remote-tracking branch 'upstream/main'
commit
182551692f
73
src/body.ts
73
src/body.ts
|
|
@ -460,6 +460,17 @@ export interface BodyData {
|
|||
>
|
||||
}
|
||||
|
||||
export interface HandData {
|
||||
child: Record<
|
||||
string,
|
||||
{
|
||||
position?: ReturnType<THREE.Vector3['toArray']>
|
||||
rotation?: ReturnType<THREE.Euler['toArray']>
|
||||
scale?: ReturnType<THREE.Vector3['toArray']>
|
||||
}
|
||||
>
|
||||
}
|
||||
|
||||
export class BodyControlor {
|
||||
body: Object3D
|
||||
part: Record<ControlPartName, Object3D> = {} as any
|
||||
|
|
@ -983,6 +994,68 @@ export class BodyControlor {
|
|||
this.Update()
|
||||
}
|
||||
|
||||
GetHandData(hand: 'left_hand' | 'right_hand'): HandData {
|
||||
const o = this.part[hand]
|
||||
const result: HandData = {
|
||||
child: {},
|
||||
}
|
||||
o.traverse((child) => {
|
||||
if (child.name && IsBone(child.name)) {
|
||||
if (child.name in result.child)
|
||||
console.log('Duplicate name', child.name, child)
|
||||
const data: Pick<BodyData, 'position' | 'rotation' | 'scale'> =
|
||||
{}
|
||||
|
||||
if (
|
||||
this.getDistanceOf(
|
||||
child.position,
|
||||
new THREE.Vector3(0, 0, 0)
|
||||
) != 0
|
||||
) {
|
||||
data.position = child.position.toArray()
|
||||
}
|
||||
|
||||
if (
|
||||
this.getDistanceOf(
|
||||
child.scale,
|
||||
new THREE.Vector3(1, 1, 1)
|
||||
) != 0
|
||||
) {
|
||||
data.scale = child.scale.toArray()
|
||||
}
|
||||
|
||||
if (
|
||||
child.rotation.x !== 0 ||
|
||||
child.rotation.y !== 0 ||
|
||||
child.rotation.z !== 0
|
||||
) {
|
||||
data.rotation = child.rotation.toArray()
|
||||
}
|
||||
if (data) result.child[child.name] = data
|
||||
}
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
RestoreHand(hand: 'left_hand' | 'right_hand', data: HandData) {
|
||||
data.child = Object.fromEntries(
|
||||
Object.entries(data.child).map(([k, v]) => {
|
||||
if (hand == 'left_hand') return [k.replace('_R', '_L'), v]
|
||||
if (hand == 'right_hand') return [k.replace('_L', '_R'), v]
|
||||
return [k, v]
|
||||
})
|
||||
)
|
||||
this.part[hand]?.traverse((o) => {
|
||||
if (o.name && o.name in data.child) {
|
||||
const child = data.child[o.name]
|
||||
if (child.position) o.position.fromArray(child.position)
|
||||
if (child.rotation) o.rotation.fromArray(child.rotation as any)
|
||||
if (child.scale) o.scale.fromArray(child.scale)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
GetBodyData(): BodyData {
|
||||
const o = this.part['torso']
|
||||
const result: BodyData = {
|
||||
|
|
|
|||
|
|
@ -79,6 +79,18 @@ const MenubarDemo: React.FC<{
|
|||
>
|
||||
{i18n.t('Save Scene')}
|
||||
</Menubar.Item>
|
||||
<Menubar.Item
|
||||
className={MenubarItem}
|
||||
onSelect={() => helper.LoadGesture()}
|
||||
>
|
||||
{i18n.t('Load Gesture')}
|
||||
</Menubar.Item>
|
||||
<Menubar.Item
|
||||
className={MenubarItem}
|
||||
onSelect={() => helper.SaveGesture()}
|
||||
>
|
||||
{i18n.t('Save Gesture')}
|
||||
</Menubar.Item>
|
||||
<Menubar.Item
|
||||
className={MenubarItem}
|
||||
onSelect={() => {
|
||||
|
|
@ -290,6 +302,21 @@ const MenubarDemo: React.FC<{
|
|||
</Menubar.ItemIndicator>
|
||||
{i18n.t('Show Preview')}
|
||||
</Menubar.CheckboxItem>
|
||||
<Menubar.CheckboxItem
|
||||
className={classNames(MenubarCheckboxItem, inset)}
|
||||
checked={editor.EnableHelper}
|
||||
onCheckedChange={() => {
|
||||
editor.EnableHelper = !editor.EnableHelper
|
||||
forceUpdate()
|
||||
}}
|
||||
>
|
||||
<Menubar.ItemIndicator
|
||||
className={MenubarItemIndicator}
|
||||
>
|
||||
<CheckIcon />
|
||||
</Menubar.ItemIndicator>
|
||||
{i18n.t('Show Grid')}
|
||||
</Menubar.CheckboxItem>
|
||||
</Menubar.Content>
|
||||
</Menubar.Portal>
|
||||
</Menubar.Menu>
|
||||
|
|
|
|||
|
|
@ -243,6 +243,8 @@ export class BodyEditor {
|
|||
effectSobel?: ShaderPass
|
||||
enableComposer = false
|
||||
enablePreview = true
|
||||
enableHelper = true
|
||||
|
||||
paused = false
|
||||
|
||||
parentElem: ParentElement
|
||||
|
|
@ -1153,11 +1155,23 @@ export class BodyEditor {
|
|||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
return () => {}
|
||||
}
|
||||
changeHelper() {
|
||||
const old = {
|
||||
axesHelper: this.axesHelper.visible,
|
||||
gridHelper: this.gridHelper.visible,
|
||||
}
|
||||
this.axesHelper.visible = false
|
||||
this.gridHelper.visible = false
|
||||
|
||||
return () => {
|
||||
this.axesHelper.visible = old.axesHelper
|
||||
this.gridHelper.visible = old.gridHelper
|
||||
}
|
||||
}
|
||||
MakeImages() {
|
||||
this.renderer.setClearColor(0x000000)
|
||||
|
||||
this.axesHelper.visible = false
|
||||
this.gridHelper.visible = false
|
||||
const restoreHelper = this.changeHelper()
|
||||
|
||||
const restoreTransfromControl = this.changeTransformControl()
|
||||
const restoreView = this.changeView()
|
||||
|
|
@ -1173,8 +1187,7 @@ export class BodyEditor {
|
|||
/// end
|
||||
|
||||
this.renderer.setClearColor(0x000000, 0)
|
||||
this.axesHelper.visible = true
|
||||
this.gridHelper.visible = true
|
||||
restoreHelper()
|
||||
|
||||
restoreTransfromControl()
|
||||
restoreView()
|
||||
|
|
@ -1250,7 +1263,7 @@ export class BodyEditor {
|
|||
}
|
||||
|
||||
getSelectedBody() {
|
||||
let obj: Object3D | null = this.transformControl.object ?? null
|
||||
let obj: Object3D | null = this.getSelectedPart() ?? null
|
||||
obj = obj ? this.getBodyByPart(obj) : null
|
||||
|
||||
return obj
|
||||
|
|
@ -1258,6 +1271,19 @@ export class BodyEditor {
|
|||
getSelectedPart() {
|
||||
return this.transformControl.object
|
||||
}
|
||||
|
||||
getHandByPart(o: Object3D) {
|
||||
if (IsHand(o?.name)) return o
|
||||
|
||||
const body = this.getAncestors(o).find((o) => IsHand(o?.name)) ?? null
|
||||
return body
|
||||
}
|
||||
|
||||
getSelectedHand() {
|
||||
let obj: Object3D | null = this.getSelectedPart() ?? null
|
||||
obj = obj ? this.getHandByPart(obj) : null
|
||||
return obj
|
||||
}
|
||||
RemoveBody() {
|
||||
const obj = this.getSelectedBody()
|
||||
|
||||
|
|
@ -1365,6 +1391,14 @@ export class BodyEditor {
|
|||
this.setFootVisible(!this.onlyHand)
|
||||
}
|
||||
|
||||
get EnableHelper() {
|
||||
return this.enableHelper
|
||||
}
|
||||
set EnableHelper(value: boolean) {
|
||||
this.enableHelper = value
|
||||
this.gridHelper.visible = value
|
||||
this.axesHelper.visible = value
|
||||
}
|
||||
setFootVisible(value: boolean) {
|
||||
this.traverseExtremities((o) => {
|
||||
if (IsFoot(o.name)) {
|
||||
|
|
@ -1521,6 +1555,25 @@ void main() {
|
|||
|
||||
return data
|
||||
}
|
||||
GetGesture() {
|
||||
const hand = this.getSelectedHand()
|
||||
const body = this.getSelectedBody()
|
||||
|
||||
if (!hand || !body) return null
|
||||
const data = {
|
||||
header: 'Openpose Editor by Yu Zhu',
|
||||
version: __APP_VERSION__,
|
||||
object: {
|
||||
hand: new BodyControlor(body).GetHandData(
|
||||
hand.name === 'left_hand' ? 'left_hand' : 'right_hand'
|
||||
),
|
||||
},
|
||||
setting: {},
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
AutoSaveScene() {
|
||||
try {
|
||||
const rawData = localStorage.getItem('AutoSaveSceneData')
|
||||
|
|
@ -1548,6 +1601,31 @@ void main() {
|
|||
console.error(error)
|
||||
}
|
||||
}
|
||||
RestoreGesture(rawData: string) {
|
||||
const data = JSON.parse(rawData)
|
||||
|
||||
const {
|
||||
version,
|
||||
object: { hand: handData },
|
||||
setting,
|
||||
} = data
|
||||
|
||||
if (!handData) throw new Error('Invalid json')
|
||||
const hand = this.getSelectedHand()
|
||||
const body = this.getSelectedBody()
|
||||
|
||||
if (!hand || !body) throw new Error('!hand || !body')
|
||||
|
||||
new BodyControlor(body).RestoreHand(
|
||||
hand.name == 'left_hand' ? 'left_hand' : 'right_hand',
|
||||
handData
|
||||
)
|
||||
}
|
||||
SaveGesture() {
|
||||
const data = this.GetGesture()
|
||||
if (!data) throw new Error('Failed to get gesture')
|
||||
downloadJson(JSON.stringify(data), `gesture_${getCurrentTime()}.json`)
|
||||
}
|
||||
|
||||
ClearScene() {
|
||||
this.GetBodies().forEach((o) => o.removeFromParent())
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
import { getImage } from '../../utils/image'
|
||||
import { CopyTextToClipboard, uploadImage } from '../../utils/transfer'
|
||||
import {
|
||||
CopyTextToClipboard,
|
||||
uploadImage,
|
||||
uploadJson,
|
||||
} from '../../utils/transfer'
|
||||
import { DetectPosefromImage } from '../../utils/detect'
|
||||
|
||||
import { BodyControlor } from '../../body'
|
||||
|
|
@ -62,7 +66,13 @@ export class Helper {
|
|||
} catch (error) {
|
||||
loading.hide()
|
||||
|
||||
Oops(error)
|
||||
Oops(
|
||||
i18n.t(
|
||||
'If you try to detect anime characters, you may get an error. Please try again with photos.'
|
||||
) +
|
||||
'\n' +
|
||||
error
|
||||
)
|
||||
console.error(error)
|
||||
return null
|
||||
}
|
||||
|
|
@ -85,6 +95,41 @@ export class Helper {
|
|||
}
|
||||
}
|
||||
|
||||
async SaveGesture() {
|
||||
const hand = await this.editor.getSelectedHand()
|
||||
if (!hand) {
|
||||
ShowToast({ title: i18n.t('Please select a hand!!') })
|
||||
return
|
||||
}
|
||||
try {
|
||||
this.editor.SaveGesture()
|
||||
} catch (error) {
|
||||
Oops(error)
|
||||
console.error(error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
async LoadGesture() {
|
||||
const hand = await this.editor.getSelectedHand()
|
||||
|
||||
if (!hand) {
|
||||
ShowToast({ title: i18n.t('Please select a hand!!') })
|
||||
return
|
||||
}
|
||||
|
||||
const rawData = await uploadJson()
|
||||
if (!rawData) return
|
||||
|
||||
try {
|
||||
this.editor.RestoreGesture(rawData)
|
||||
} catch (error) {
|
||||
Oops(error)
|
||||
console.error(error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
async GenerateSceneURL() {
|
||||
try {
|
||||
const d = encodeURIComponent(
|
||||
|
|
|
|||
|
|
@ -62,5 +62,10 @@
|
|||
"Copy Keypoint Data": "Schlüsselpunktdaten kopieren",
|
||||
"Copied to Clipboard": "In die Zwischenablage kopiert",
|
||||
"Generate Scene URL": "Szene-URL generieren",
|
||||
"Reset Scene": "Szene zurücksetzen"
|
||||
"Reset Scene": "Szene zurücksetzen",
|
||||
"Load Gesture": "Geste laden",
|
||||
"Save Gesture": "Geste speichern",
|
||||
"Please select a hand!!": "Bitte wählen Sie eine Hand aus!!",
|
||||
"If you try to detect anime characters, you may get an error. Please try again with photos.": "Bei dem Versuch, Anime-Charaktere zu erkennen, kann es zu einem Fehler kommen. Bitte versuchen Sie es erneut mit Fotos.",
|
||||
"Show Grid": "Gitter anzeigen"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,5 +62,10 @@
|
|||
"Unlock View": "Unlock View",
|
||||
"Copy Keypoint Data": "Copy Keypoint Data",
|
||||
"Copied to Clipboard": "Copied to Clipboard",
|
||||
"Reset Scene": "Reset Scene"
|
||||
"Reset Scene": "Reset Scene",
|
||||
"Load Gesture": "Load Gesture",
|
||||
"Save Gesture": "Save Gesture",
|
||||
"Please select a hand!!": "Please select a hand!!",
|
||||
"If you try to detect anime characters, you may get an error. Please try again with photos.": "If you try to detect anime characters, you may get an error. Please try again with photos.",
|
||||
"Show Grid": "Show Grid"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,5 +62,10 @@
|
|||
"Unlock View": "ビューをアンロックする",
|
||||
"Copy Keypoint Data": "キーポイントデータをコピーする",
|
||||
"Copied to Clipboard": "クリップボードにコピーされました",
|
||||
"Reset Scene": "シーンをリセットする"
|
||||
"Reset Scene": "シーンをリセットする",
|
||||
"Load Gesture": "ジェスチャーを読み込む",
|
||||
"Save Gesture": "ジェスチャーを保存する",
|
||||
"Please select a hand!!": "手を選択してください!!",
|
||||
"If you try to detect anime characters, you may get an error. Please try again with photos.": "アニメキャラクターを検出しようとするとエラーが発生する場合があります。写真で再度お試しください。",
|
||||
"Show Grid": "グリッドを表示"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,5 +62,10 @@
|
|||
"Copy Keypoint Data": "Copiar datos del punto clave",
|
||||
"Copied to Clipboard": "Copiado al portapapeles",
|
||||
"Generate Scene URL": "Generar URL de escena",
|
||||
"Reset Scene": "Restablecer escena"
|
||||
"Reset Scene": "Restablecer escena",
|
||||
"Load Gesture": "Cargar gesto",
|
||||
"Save Gesture": "Guardar gesto",
|
||||
"Please select a hand!!": "¡¡Por favor, seleccione una mano!!",
|
||||
"If you try to detect anime characters, you may get an error. Please try again with photos.": "Si intenta detectar personajes de anime, es posible que obtenga un error. Por favor, inténtelo de nuevo con fotos.",
|
||||
"Show Grid": "Mostrar cuadrícula"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,5 +62,10 @@
|
|||
"Copy Keypoint Data": "复制关键点数据",
|
||||
"Copied to Clipboard": "已复制到剪贴板",
|
||||
"Generate Scene URL": "生成场景URL",
|
||||
"Reset Scene": "重置场景"
|
||||
"Reset Scene": "重置场景",
|
||||
"Load Gesture": "加载手势",
|
||||
"Save Gesture": "保存手势",
|
||||
"Please select a hand!!": "请选择一只手!!",
|
||||
"If you try to detect anime characters, you may get an error. Please try again with photos.": "如果您尝试检测动漫角色,可能会出现错误。请使用照片再试一次。",
|
||||
"Show Grid": "显示网格"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,5 +62,10 @@
|
|||
"Copy Keypoint Data": "複製關鍵點數據",
|
||||
"Copied to Clipboard": "已複製到剪貼板",
|
||||
"Generate Scene URL": "生成場景URL",
|
||||
"Reset Scene": "重置場景"
|
||||
"Reset Scene": "重置場景",
|
||||
"Load Gesture": "載入手勢",
|
||||
"Save Gesture": "儲存手勢",
|
||||
"Please select a hand!!": "請選擇一隻手!!",
|
||||
"If you try to detect anime characters, you may get an error. Please try again with photos.": "如果您嘗試檢測動漫角色,可能會出現錯誤。請使用照片再試一次。",
|
||||
"Show Grid": "顯示網格"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,5 +62,10 @@
|
|||
"Copy Keypoint Data": "複製關鍵點數據",
|
||||
"Copied to Clipboard": "已複製到剪貼板",
|
||||
"Generate Scene URL": "生成場景URL",
|
||||
"Reset Scene": "重置場景"
|
||||
"Reset Scene": "重置場景",
|
||||
"Load Gesture": "載入手勢",
|
||||
"Save Gesture": "儲存手勢",
|
||||
"Please select a hand!!": "請選擇一隻手!!",
|
||||
"If you try to detect anime characters, you may get an error. Please try again with photos.": "如果您嘗試檢測動漫角色,可能會出現錯誤。請使用照片再試一次。",
|
||||
"Show Grid": "顯示網格"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ const config: UserConfigFn = ({ command, mode, ssrBuild }) => {
|
|||
base: mode === 'singlefile' ? './' : '/open-pose-editor/',
|
||||
define: {
|
||||
global: {},
|
||||
__APP_VERSION__: JSON.stringify('0.1.16'),
|
||||
__APP_VERSION__: JSON.stringify('0.1.18'),
|
||||
__APP_BUILD_TIME__: Date.now(),
|
||||
},
|
||||
build: {
|
||||
|
|
|
|||
Loading…
Reference in New Issue