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
## 目的 资源画布支持引擎(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
150 lines
4.9 KiB
TypeScript
150 lines
4.9 KiB
TypeScript
import { useEffect, useRef, useState } from 'react';
|
||
|
||
import { renderResourceModelThumbnail } from './resourceModelThumbnail';
|
||
|
||
type ResourceModelPreviewProps = {
|
||
/** 预览管线给出的模型字节 URL(blob URL)。 */
|
||
sourceUrl: string;
|
||
/** 原生侧判定的媒体类型(`model/gltf-binary` / `model/gltf+json` / FBX 的 octet-stream)。 */
|
||
mediaType: string;
|
||
/** 缩略图缓存身份(见 `resourceModelThumbnailIdentity`):blob URL 会变,身份不会。 */
|
||
identity: string;
|
||
/** 渲染不出来时画在同一个位置上的类型卡文案(例如「模型 · GLB」)。 */
|
||
fallbackLabel: string;
|
||
};
|
||
|
||
/**
|
||
* 缩略图超采样倍数。
|
||
*
|
||
* 资源画布用 `transform: scale(--resource-section-zoom)` 放大栏目视图(真机 1.5 倍),而
|
||
* `ResizeObserver` 与 `offsetWidth` 都只看**布局盒**:按布局尺寸 1:1 渲染出来的图,被画布
|
||
* 放大后就是糊的。按 2 倍超采样既有余量覆盖画布缩放,也不至于让单张缩略图失控。
|
||
*/
|
||
const RESOURCE_MODEL_THUMBNAIL_SUPERSAMPLE = 2;
|
||
const RESOURCE_MODEL_THUMBNAIL_MAX_EDGE = 1024;
|
||
|
||
/**
|
||
* 资源卡上的引擎模型缩略图。
|
||
*
|
||
* 这一层只关心「把渲染结果画出来」,渲染本身在 `resourceModelThumbnail` 的单例队列里
|
||
* (整页只有一个 WebGL 上下文)。渲染失败**不算预览失败**:卡面就地降级成类型卡,
|
||
* 预览管线里已经读到的字节与状态保持不变,排障信息通过
|
||
* `data-model-preview-status` 暴露在 DOM 上。
|
||
*/
|
||
export function ResourceModelPreview({
|
||
sourceUrl,
|
||
mediaType,
|
||
identity,
|
||
fallbackLabel,
|
||
}: ResourceModelPreviewProps) {
|
||
const hostRef = useRef<HTMLSpanElement>(null);
|
||
const [thumbnail, setThumbnail] = useState<string | null>(null);
|
||
const [status, setStatus] = useState<'loading' | 'ready' | 'failed'>(
|
||
'loading',
|
||
);
|
||
/**
|
||
* 渲染尺寸按**宿主真实几何**(含画布缩放后的视觉尺寸)取,并且跟着宿主的尺寸变化重渲。
|
||
*
|
||
* 卡片在总览小图与栏目大图里是同一个组件:只在挂载时量一次,会拿到当时那个更小的盒子,
|
||
* 之后卡片放大也不会重画 —— 表现为缩略图被放大到发虚、比例与卡面不一致。
|
||
*/
|
||
const [renderSize, setRenderSize] = useState<{
|
||
width: number;
|
||
height: number;
|
||
} | null>(null);
|
||
|
||
useEffect(() => {
|
||
const host = hostRef.current;
|
||
if (!host) {
|
||
return undefined;
|
||
}
|
||
const measure = () => {
|
||
/*
|
||
* 取**布局尺寸**(`offsetWidth` 不受祖先 transform 影响)而不是 `getBoundingClientRect`:
|
||
* 画布缩放是 transform,像素值会随缩放变化;布局尺寸才是稳定的口径,再乘超采样倍数。
|
||
*/
|
||
const width = Math.min(
|
||
RESOURCE_MODEL_THUMBNAIL_MAX_EDGE,
|
||
Math.max(
|
||
192,
|
||
Math.round(
|
||
(host.offsetWidth || 320) * RESOURCE_MODEL_THUMBNAIL_SUPERSAMPLE,
|
||
),
|
||
),
|
||
);
|
||
const height = Math.min(
|
||
RESOURCE_MODEL_THUMBNAIL_MAX_EDGE,
|
||
Math.max(
|
||
144,
|
||
Math.round(
|
||
(host.offsetHeight || 240) * RESOURCE_MODEL_THUMBNAIL_SUPERSAMPLE,
|
||
),
|
||
),
|
||
);
|
||
setRenderSize((current) =>
|
||
current && current.width === width && current.height === height
|
||
? current
|
||
: { width, height },
|
||
);
|
||
};
|
||
measure();
|
||
// 测试用的 jsdom 没有 ResizeObserver:没有它就退化成"只在挂载时量一次"。
|
||
if (typeof ResizeObserver === 'undefined') {
|
||
return undefined;
|
||
}
|
||
const observer = new ResizeObserver(measure);
|
||
observer.observe(host);
|
||
return () => observer.disconnect();
|
||
}, []);
|
||
|
||
useEffect(() => {
|
||
if (!renderSize) {
|
||
return undefined;
|
||
}
|
||
let disposed = false;
|
||
setThumbnail(null);
|
||
setStatus('loading');
|
||
const request = {
|
||
sourceUrl,
|
||
mediaType,
|
||
identity,
|
||
width: renderSize.width,
|
||
height: renderSize.height,
|
||
};
|
||
void renderResourceModelThumbnail(request)
|
||
.then((dataUrl) => {
|
||
if (disposed) {
|
||
return;
|
||
}
|
||
setThumbnail(dataUrl);
|
||
setStatus('ready');
|
||
})
|
||
.catch(() => {
|
||
if (!disposed) {
|
||
setStatus('failed');
|
||
}
|
||
});
|
||
return () => {
|
||
disposed = true;
|
||
};
|
||
}, [identity, mediaType, renderSize, sourceUrl]);
|
||
|
||
return (
|
||
<span
|
||
ref={hostRef}
|
||
className="game-resource-card-model-visual"
|
||
data-model-preview-status={status}
|
||
// 渲染尺寸暴露到 DOM:缩略图糊 / 比例不对时,先看它和卡片实际几何是否一致。
|
||
data-model-render-size={
|
||
renderSize ? `${renderSize.width}x${renderSize.height}` : undefined
|
||
}
|
||
>
|
||
{thumbnail ? (
|
||
<img src={thumbnail} alt="" decoding="async" />
|
||
) : (
|
||
<span className="game-resource-card-engine-badge">{fallbackLabel}</span>
|
||
)}
|
||
</span>
|
||
);
|
||
}
|