@agent-os-lab/agent-game-sdk 0.1.14 → 0.1.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -0
- package/USAGE.md +9 -0
- package/package.json +1 -1
- package/src/avatar/three-scene.ts +52 -9
- package/src/avatar/three-thumbnail.ts +17 -2
- package/src/avatar/three-view.ts +8 -2
package/README.md
CHANGED
|
@@ -91,9 +91,16 @@ import { mountAgentAvatar3D } from "@agent-os-lab/agent-game-sdk/avatar";
|
|
|
91
91
|
|
|
92
92
|
const view = mountAgentAvatar3D(container, {
|
|
93
93
|
agentId: "agent-1",
|
|
94
|
+
backgroundColor: "#f8fafc",
|
|
94
95
|
sceneState: "working",
|
|
95
96
|
framing: "upperBody",
|
|
96
97
|
viewAngle: "front",
|
|
98
|
+
camera: {
|
|
99
|
+
fov: 32,
|
|
100
|
+
positionY: 2.1,
|
|
101
|
+
positionZ: 1.35,
|
|
102
|
+
lookAtY: 1.42,
|
|
103
|
+
},
|
|
97
104
|
});
|
|
98
105
|
|
|
99
106
|
view.update({ sceneState: "thinking", framing: "fullBody", viewAngle: "threeQuarter" });
|
package/USAGE.md
CHANGED
|
@@ -188,9 +188,16 @@ import { mountAgentAvatar3D } from "@agent-os-lab/agent-game-sdk/avatar";
|
|
|
188
188
|
|
|
189
189
|
const view = mountAgentAvatar3D(container, {
|
|
190
190
|
agentId: "agent-1",
|
|
191
|
+
backgroundColor: "#f8fafc",
|
|
191
192
|
sceneState: "working",
|
|
192
193
|
framing: "upperBody",
|
|
193
194
|
viewAngle: "front",
|
|
195
|
+
camera: {
|
|
196
|
+
fov: 32,
|
|
197
|
+
positionY: 2.1,
|
|
198
|
+
positionZ: 1.35,
|
|
199
|
+
lookAtY: 1.42,
|
|
200
|
+
},
|
|
194
201
|
});
|
|
195
202
|
|
|
196
203
|
view.update({
|
|
@@ -203,6 +210,8 @@ view.destroy();
|
|
|
203
210
|
```
|
|
204
211
|
|
|
205
212
|
`framing` supports `fullBody` and `upperBody`. `viewAngle` supports `front` and `threeQuarter`.
|
|
213
|
+
The default 3D avatar background is transparent. Pass `backgroundColor` when the canvas or thumbnail should render with a solid color.
|
|
214
|
+
Use `camera` to fine-tune the portrait crop with `fov`, `positionY`, `positionZ`, and `lookAtY`.
|
|
206
215
|
Passing the same `agentId` used in the office game gives the standalone 3D view the same appearance as the office game scene. Use `renderIndex` only when you need to override that AgentID-derived appearance.
|
|
207
216
|
|
|
208
217
|
### Static 3D Thumbnails
|
package/package.json
CHANGED
|
@@ -17,8 +17,19 @@ import {
|
|
|
17
17
|
import { createAgentMesh, type AgentMeshParts } from "../office/renderers/three/agent-mesh";
|
|
18
18
|
import type { AgentAvatar3DFraming, AgentAvatar3DViewAngle } from "./three-view";
|
|
19
19
|
|
|
20
|
+
export type AgentAvatar3DCameraOptions = {
|
|
21
|
+
fov?: number;
|
|
22
|
+
positionY?: number;
|
|
23
|
+
positionZ?: number;
|
|
24
|
+
lookAtY?: number;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export type ResolvedAgentAvatar3DCameraOptions = Required<AgentAvatar3DCameraOptions>;
|
|
28
|
+
|
|
20
29
|
export type AgentAvatar3DSceneOptions = {
|
|
21
30
|
agent?: Partial<AgentGameOfficeAgent>;
|
|
31
|
+
backgroundColor?: THREE.ColorRepresentation | null;
|
|
32
|
+
camera?: AgentAvatar3DCameraOptions;
|
|
22
33
|
agentId?: string;
|
|
23
34
|
framing?: AgentAvatar3DFraming;
|
|
24
35
|
sceneState?: AgentGameOfficeSceneState;
|
|
@@ -29,6 +40,7 @@ export type AgentAvatar3DSceneOptions = {
|
|
|
29
40
|
};
|
|
30
41
|
|
|
31
42
|
export type ResolvedAgentAvatar3DState = AgentGameOfficeAgent & {
|
|
43
|
+
camera: ResolvedAgentAvatar3DCameraOptions;
|
|
32
44
|
framing: AgentAvatar3DFraming;
|
|
33
45
|
renderIndex: number;
|
|
34
46
|
viewAngle: AgentAvatar3DViewAngle;
|
|
@@ -41,6 +53,7 @@ export type AgentAvatar3DScene = {
|
|
|
41
53
|
bodyLayer: AgentBodyInstancedLayer;
|
|
42
54
|
effectLayer: AgentEffectInstancedLayer;
|
|
43
55
|
state: ResolvedAgentAvatar3DState;
|
|
56
|
+
setBackground(backgroundColor?: THREE.ColorRepresentation | null): void;
|
|
44
57
|
setState(options: AgentAvatar3DSceneOptions, preservedRenderIndex?: number): ResolvedAgentAvatar3DState;
|
|
45
58
|
render(renderer: { render(scene: THREE.Scene, camera: THREE.Camera): void }, nowMs: number): void;
|
|
46
59
|
dispose(): void;
|
|
@@ -48,7 +61,7 @@ export type AgentAvatar3DScene = {
|
|
|
48
61
|
|
|
49
62
|
export function createAgentAvatar3DScene(options: AgentAvatar3DSceneOptions): AgentAvatar3DScene {
|
|
50
63
|
const scene = new THREE.Scene();
|
|
51
|
-
scene
|
|
64
|
+
applyAvatar3DBackground(scene, options.backgroundColor);
|
|
52
65
|
const camera = new THREE.PerspectiveCamera(38, options.width / options.height, 0.1, 100);
|
|
53
66
|
scene.add(new THREE.HemisphereLight(0xffffff, 0xd9e2ef, 1.8));
|
|
54
67
|
const keyLight = new THREE.DirectionalLight(0xffffff, 1.4);
|
|
@@ -59,7 +72,7 @@ export function createAgentAvatar3DScene(options: AgentAvatar3DSceneOptions): Ag
|
|
|
59
72
|
const mesh = createAgentMesh(state, state.renderIndex);
|
|
60
73
|
mesh.group.position.set(0, 0, 0);
|
|
61
74
|
applyAvatar3DViewAngle(mesh.group, state.viewAngle);
|
|
62
|
-
|
|
75
|
+
applyAvatar3DCamera(camera, state.camera);
|
|
63
76
|
scene.add(mesh.group);
|
|
64
77
|
const bodyLayer = createAgentBodyInstancedLayer(scene);
|
|
65
78
|
const effectLayer = createAgentEffectInstancedLayer(scene);
|
|
@@ -73,10 +86,16 @@ export function createAgentAvatar3DScene(options: AgentAvatar3DSceneOptions): Ag
|
|
|
73
86
|
get state() {
|
|
74
87
|
return state;
|
|
75
88
|
},
|
|
89
|
+
setBackground(backgroundColor) {
|
|
90
|
+
applyAvatar3DBackground(scene, backgroundColor);
|
|
91
|
+
},
|
|
76
92
|
setState(nextOptions, preservedRenderIndex) {
|
|
77
93
|
state = resolveAgentAvatar3DState(nextOptions, preservedRenderIndex);
|
|
94
|
+
if ("backgroundColor" in nextOptions) {
|
|
95
|
+
applyAvatar3DBackground(scene, nextOptions.backgroundColor);
|
|
96
|
+
}
|
|
78
97
|
applyAvatar3DViewAngle(mesh.group, state.viewAngle);
|
|
79
|
-
|
|
98
|
+
applyAvatar3DCamera(camera, state.camera);
|
|
80
99
|
return state;
|
|
81
100
|
},
|
|
82
101
|
render(renderer, nowMs) {
|
|
@@ -106,6 +125,7 @@ export function resolveAgentAvatar3DState(
|
|
|
106
125
|
};
|
|
107
126
|
return {
|
|
108
127
|
...agent,
|
|
128
|
+
camera: resolveAgentAvatar3DCamera(options.framing ?? "fullBody", options.camera),
|
|
109
129
|
framing: options.framing ?? "fullBody",
|
|
110
130
|
sceneState: options.sceneState ?? agent.sceneState,
|
|
111
131
|
renderIndex: options.renderIndex ?? preservedRenderIndex ?? resolveAgentAvatarRenderIndex(agent.id),
|
|
@@ -113,15 +133,38 @@ export function resolveAgentAvatar3DState(
|
|
|
113
133
|
};
|
|
114
134
|
}
|
|
115
135
|
|
|
116
|
-
function
|
|
136
|
+
function applyAvatar3DBackground(scene: THREE.Scene, backgroundColor?: THREE.ColorRepresentation | null): void {
|
|
137
|
+
scene.background = backgroundColor === undefined || backgroundColor === null
|
|
138
|
+
? null
|
|
139
|
+
: new THREE.Color(backgroundColor);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function resolveAgentAvatar3DCamera(
|
|
143
|
+
framing: AgentAvatar3DFraming,
|
|
144
|
+
camera?: AgentAvatar3DCameraOptions,
|
|
145
|
+
): ResolvedAgentAvatar3DCameraOptions {
|
|
117
146
|
if (framing === "upperBody") {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
147
|
+
return {
|
|
148
|
+
fov: camera?.fov ?? 32,
|
|
149
|
+
positionY: camera?.positionY ?? 2.1,
|
|
150
|
+
positionZ: camera?.positionZ ?? 1.35,
|
|
151
|
+
lookAtY: camera?.lookAtY ?? 1.42,
|
|
152
|
+
};
|
|
121
153
|
}
|
|
122
154
|
|
|
123
|
-
|
|
124
|
-
|
|
155
|
+
return {
|
|
156
|
+
fov: camera?.fov ?? 38,
|
|
157
|
+
positionY: camera?.positionY ?? 2.1,
|
|
158
|
+
positionZ: camera?.positionZ ?? 5.2,
|
|
159
|
+
lookAtY: camera?.lookAtY ?? 1.05,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function applyAvatar3DCamera(camera: THREE.PerspectiveCamera, options: ResolvedAgentAvatar3DCameraOptions): void {
|
|
164
|
+
camera.fov = options.fov;
|
|
165
|
+
camera.position.set(0, options.positionY, options.positionZ);
|
|
166
|
+
camera.lookAt(0, options.lookAtY, 0);
|
|
167
|
+
camera.updateProjectionMatrix();
|
|
125
168
|
}
|
|
126
169
|
|
|
127
170
|
function applyAvatar3DViewAngle(group: THREE.Group, viewAngle: AgentAvatar3DViewAngle): void {
|
|
@@ -47,6 +47,7 @@ export type AgentAvatar3DThumbnailMountOptions = AgentAvatar3DThumbnailOptions &
|
|
|
47
47
|
export type AgentAvatar3DThumbnailController = {
|
|
48
48
|
image: HTMLImageElement;
|
|
49
49
|
thumbnail: AgentAvatar3DThumbnail;
|
|
50
|
+
update(options: AgentAvatar3DThumbnailOptions): AgentAvatar3DThumbnail;
|
|
50
51
|
destroy(): void;
|
|
51
52
|
};
|
|
52
53
|
|
|
@@ -100,7 +101,8 @@ export function mountAgentAvatar3DThumbnail(
|
|
|
100
101
|
container: HTMLElement,
|
|
101
102
|
options: AgentAvatar3DThumbnailMountOptions,
|
|
102
103
|
): AgentAvatar3DThumbnailController {
|
|
103
|
-
const
|
|
104
|
+
const renderer = createAgentAvatar3DThumbnailRenderer(options);
|
|
105
|
+
let thumbnail = renderer.render(options);
|
|
104
106
|
const image = options.createImage?.() ?? new Image();
|
|
105
107
|
image.src = thumbnail.dataUrl;
|
|
106
108
|
image.width = thumbnail.width;
|
|
@@ -109,8 +111,21 @@ export function mountAgentAvatar3DThumbnail(
|
|
|
109
111
|
|
|
110
112
|
return {
|
|
111
113
|
image,
|
|
112
|
-
thumbnail
|
|
114
|
+
get thumbnail() {
|
|
115
|
+
return thumbnail;
|
|
116
|
+
},
|
|
117
|
+
update(nextOptions) {
|
|
118
|
+
thumbnail = renderer.render({
|
|
119
|
+
...options,
|
|
120
|
+
...nextOptions,
|
|
121
|
+
});
|
|
122
|
+
image.src = thumbnail.dataUrl;
|
|
123
|
+
image.width = thumbnail.width;
|
|
124
|
+
image.height = thumbnail.height;
|
|
125
|
+
return thumbnail;
|
|
126
|
+
},
|
|
113
127
|
destroy() {
|
|
128
|
+
renderer.destroy();
|
|
114
129
|
image.remove();
|
|
115
130
|
},
|
|
116
131
|
};
|
package/src/avatar/three-view.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type { AgentGameOfficeAgent, AgentGameOfficeSceneState } from "../office/
|
|
|
4
4
|
import { AgentGameError } from "../core/errors";
|
|
5
5
|
import {
|
|
6
6
|
createAgentAvatar3DScene,
|
|
7
|
+
type AgentAvatar3DCameraOptions,
|
|
7
8
|
type ResolvedAgentAvatar3DState,
|
|
8
9
|
} from "./three-scene";
|
|
9
10
|
|
|
@@ -20,6 +21,8 @@ export type AgentAvatar3DViewAngle = "front" | "threeQuarter";
|
|
|
20
21
|
|
|
21
22
|
export type AgentAvatar3DOptions = {
|
|
22
23
|
agent?: Partial<AgentGameOfficeAgent>;
|
|
24
|
+
backgroundColor?: THREE.ColorRepresentation | null;
|
|
25
|
+
camera?: AgentAvatar3DCameraOptions;
|
|
23
26
|
agentId?: string;
|
|
24
27
|
framing?: AgentAvatar3DFraming;
|
|
25
28
|
sceneState?: AgentGameOfficeSceneState;
|
|
@@ -36,7 +39,7 @@ export type AgentAvatar3DOptions = {
|
|
|
36
39
|
|
|
37
40
|
export type AgentAvatar3DUpdateOptions = Partial<Pick<
|
|
38
41
|
AgentAvatar3DOptions,
|
|
39
|
-
"agent" | "agentId" | "framing" | "sceneState" | "renderIndex" | "viewAngle"
|
|
42
|
+
"agent" | "agentId" | "backgroundColor" | "camera" | "framing" | "sceneState" | "renderIndex" | "viewAngle"
|
|
40
43
|
>>;
|
|
41
44
|
|
|
42
45
|
export type AgentAvatar3DController = {
|
|
@@ -47,7 +50,7 @@ export type AgentAvatar3DController = {
|
|
|
47
50
|
getState(): ResolvedAgentAvatar3DState;
|
|
48
51
|
};
|
|
49
52
|
|
|
50
|
-
export type { ResolvedAgentAvatar3DState } from "./three-scene";
|
|
53
|
+
export type { AgentAvatar3DCameraOptions, ResolvedAgentAvatar3DState } from "./three-scene";
|
|
51
54
|
|
|
52
55
|
export function mountAgentAvatar3D(
|
|
53
56
|
container: HTMLElement,
|
|
@@ -84,10 +87,13 @@ export function mountAgentAvatar3D(
|
|
|
84
87
|
throw new AgentGameError("runtime_destroyed", "Agent avatar 3D view has been destroyed");
|
|
85
88
|
}
|
|
86
89
|
const agentIdChanged = nextOptions.agentId !== undefined || nextOptions.agent?.id !== undefined;
|
|
90
|
+
const framingChanged = nextOptions.framing !== undefined && nextOptions.framing !== avatarScene.state.framing;
|
|
87
91
|
const preserveRenderIndex = hasExplicitRenderIndex && nextOptions.renderIndex === undefined && !agentIdChanged;
|
|
88
92
|
avatarScene.setState({
|
|
89
93
|
agent: { ...avatarScene.state, ...nextOptions.agent },
|
|
90
94
|
agentId: nextOptions.agentId,
|
|
95
|
+
backgroundColor: nextOptions.backgroundColor,
|
|
96
|
+
camera: nextOptions.camera ?? (framingChanged ? undefined : avatarScene.state.camera),
|
|
91
97
|
framing: nextOptions.framing ?? avatarScene.state.framing,
|
|
92
98
|
sceneState: nextOptions.sceneState ?? avatarScene.state.sceneState,
|
|
93
99
|
renderIndex: nextOptions.renderIndex,
|