Files
Genarrative/apps/ai-game-creator-shell/src/view/project-development/ResourceModelPreviewDialog.tsx
T
kdletters d5743cd4b8
Project CI / AI game creator shell Rust shard 1/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 2/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 3/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 4/4 (push) Has been cancelled
Project CI / AI game creator shell Rust smoke (push) Has been cancelled
Project CI / AI game creator shell Rust crates (push) Has been cancelled
Project CI / Backend tests (push) Has been cancelled
Project CI / Native shell tests (push) Has been cancelled
Project CI / Frontend tests (push) Has been cancelled
Project CI / Repository checks (push) Has been cancelled
Project CI / AI game creator shell web tests (push) Has been cancelled
资源画布支持引擎资源预览与模型交互预览 (#413)
## 目的

资源画布支持引擎(Cocos Creator)资源的**只读预览**:能被发现、登记、进入画布,并按类型出预览。

## 主要改动

- 发现层识别 Cocos 资源并区分 `model` / `binary` 两个发现类别;`.meta` 等导入侧车文件仍只可发现
- 登记层按扩展名写入**既有** canonical kind(模型/场景/预制体 → `scene`,动画 → `character-animation`,材质/特效 → `code`,图集与容器 → `document`),不新增 manifest 契约字段
- 引擎工程的 `library/` / `temp/` / `profiles/` / `local/` 不再进入发现结果(仅当目录确实是 Cocos 工程),同名目录在非引擎工程里照常列出
- 资源画布新增三个卡面分支:模型缩略图(整页共用一个 WebGL 上下文)、结构摘要(Cocos 序列化资源)、类型卡(客户端解不了的容器)
- 新增模型放大预览浮层:左键旋转 / 右键或中键平移 / 滚轮缩放 / 复位视角
- 模型预览上限 32 MiB;缩略图按布局尺寸 2 倍超采样,缓存键改用稳定身份
- 引擎图像容器(tga/tif/tiff/hdr)在原生侧转码成 PNG 后复用既有图片预览链路
- 修复资源卡状态边框吃掉内容盒导致的卡面与角标位移
- 同步 Agent 提示词与 AGC 技能文档,并补里程碑与踩坑文档

## 验证

- `cargo check`、`cargo fmt --check`
- Rust 定向:`cargo test … cocos` 9 条、`resource_inspect::tests` 7 条、`agent_asset_import_tests` 9 条、生成目录过滤用例
- 前端:`resource` + `project` 套件 52 文件 / 546 用例;`appSurface` 450 通过 / 20 跳过;app `tsc --noEmit`
- `npm run check:encoding`、`git diff --check`、`npm run check:doc-index`
- 真机:在真实客户端内打开本地 Cocos 夹具工程,逐栏核对模型缩略图 / 结构摘要 / TGA 转码 / 类型卡,并量过卡片在指针移开、悬停、选中三态下几何完全一致

## 未验证

- 模型缩略图与放大预览的真机视觉只覆盖自带夹具工程;多文件 glTF(外部 .bin/贴图)与超大模型仍是类型卡

---------

Co-authored-by: kdletters <61648117+kdletters@users.noreply.github.com>
Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/413
2026-09-17 20:28:39 +08:00

115 lines
3.9 KiB
TypeScript

import { Maximize2, RotateCcw, X } from 'lucide-react';
import { useCallback, useEffect, useRef, useState } from 'react';
import { PlatformActionButton } from '../../../../../packages/shared/src/components/PlatformActionButton';
import { PlatformIconButton } from '../../../../../packages/shared/src/components/PlatformIconButton';
import { ThemedModal } from '../../components/modal/ThemedModal';
import type { ProjectResourceCardPreviewState } from './resourceCardPreviewModel';
import {
ResourceModelViewer,
type ResourceModelViewerHandle,
} from './ResourceModelViewer';
import type { ProjectResource } from './resourceProjectionModel';
/**
* 引擎模型的**放大预览浮层**:在独立浮层里给出与 3D 建模软件一致的视角操作。
*
* 卡片上仍然是静态缩略图(一张画布上十几张模型卡共用缩略图那一个 WebGL 上下文);
* 只有打开这个浮层时才会新建一个交互式上下文,关闭即释放。浮层内的指针与滚轮事件由
* three 的 OrbitControls 独占:左键旋转、右键/中键平移、滚轮推拉。
*/
export function ResourceModelPreviewDialog({
resource,
identity,
preview,
onRequestPreview,
onClose,
}: {
resource: ProjectResource;
identity: string;
preview: ProjectResourceCardPreviewState;
onRequestPreview: (
resource: ProjectResource,
identity: string,
reason: 'detail',
) => void;
onClose: () => void;
}) {
const [resetSignal, setResetSignal] = useState(0);
const viewerHandleRef = useRef<ResourceModelViewerHandle | null>(null);
useEffect(() => {
onRequestPreview(resource, identity, 'detail');
}, [resource, identity, onRequestPreview]);
const handleReady = useCallback((handle: ResourceModelViewerHandle) => {
viewerHandleRef.current = handle;
}, []);
const sourceUrl =
preview.status === 'loaded' ? (preview.preview.sourceUrl ?? null) : null;
return (
<ThemedModal
open
ariaLabel="模型预览"
onClose={onClose}
panelClassName="game-resource-model-preview"
>
<header className="game-resource-model-preview__header">
<strong title={resource.label}>{resource.label}</strong>
<div className="game-resource-model-preview__actions">
<PlatformActionButton
tone="secondary"
onClick={() => setResetSignal((current) => current + 1)}
>
<RotateCcw size={15} aria-hidden="true" />
<span>复位视角</span>
</PlatformActionButton>
<PlatformIconButton
label="关闭模型预览"
title="关闭"
icon={<X size={18} aria-hidden="true" />}
onClick={onClose}
/>
</div>
</header>
<div className="game-resource-model-preview__body">
{preview.status === 'failed' ? (
<>
<p role="alert">{preview.error}</p>
{preview.retryable ? (
<PlatformActionButton
tone="secondary"
onClick={() => onRequestPreview(resource, identity, 'detail')}
>
重试
</PlatformActionButton>
) : null}
</>
) : preview.status === 'loaded' && !sourceUrl ? (
<p role="alert">没有可预览的模型内容</p>
) : sourceUrl ? (
<ResourceModelViewer
source={{
sourceUrl,
mediaType:
preview.status === 'loaded'
? preview.preview.mediaType
: resource.mediaType,
}}
resetSignal={resetSignal}
onReady={handleReady}
/>
) : (
<p role="status">正在加载模型…</p>
)}
<p className="game-resource-model-preview__hint">
<Maximize2 size={13} aria-hidden="true" />
<span>左键拖拽旋转 · 右键或中键拖拽平移 · 滚轮缩放</span>
</p>
</div>
</ThemedModal>
);
}