Spaces:
Runtime error
Runtime error
Commit
•
a438bb5
1
Parent(s):
bf8d4d8
fix
Browse files- src/app/games/city.ts +3 -1
- src/app/games/doom.ts +4 -1
- src/app/games/dungeon.ts +5 -4
- src/app/games/enchanters.ts +32 -0
- src/app/games/index.ts +4 -2
- src/app/games/pirates.ts +4 -1
- src/app/games/types.ts +1 -2
- src/app/games/vernian.ts +35 -0
- src/app/main.tsx +29 -20
- src/app/render.ts +26 -3
- src/components/business/renderer.tsx +2 -2
- src/lib/fonts.ts +15 -2
src/app/games/city.ts
CHANGED
@@ -49,10 +49,12 @@ export const game: Game = {
|
|
49 |
initialActionnables,
|
50 |
getScenePrompt: (situation?: string) => [
|
51 |
`isometrical bird view of 3D rendered city`,
|
52 |
-
situation || initialSituation,
|
53 |
`game screenshot`,
|
|
|
|
|
54 |
`isometric`,
|
55 |
`unreal engine`,
|
56 |
`high res`,
|
|
|
57 |
]
|
58 |
}
|
|
|
49 |
initialActionnables,
|
50 |
getScenePrompt: (situation?: string) => [
|
51 |
`isometrical bird view of 3D rendered city`,
|
|
|
52 |
`game screenshot`,
|
53 |
+
`strategy game`,
|
54 |
+
`simulator`,
|
55 |
`isometric`,
|
56 |
`unreal engine`,
|
57 |
`high res`,
|
58 |
+
situation || initialSituation,
|
59 |
]
|
60 |
}
|
src/app/games/doom.ts
CHANGED
@@ -22,8 +22,11 @@ export const game: Game = {
|
|
22 |
initialActionnables,
|
23 |
getScenePrompt: (situation?: string) => [
|
24 |
`Screenshot from Doom`,
|
|
|
|
|
|
|
|
|
25 |
situation || initialSituation,
|
26 |
-
`first person, beautiful, unreal engine`
|
27 |
]
|
28 |
}
|
29 |
|
|
|
22 |
initialActionnables,
|
23 |
getScenePrompt: (situation?: string) => [
|
24 |
`Screenshot from Doom`,
|
25 |
+
`first person`,
|
26 |
+
`shooter game`,
|
27 |
+
`science fiction`,
|
28 |
+
`unreal engine`,
|
29 |
situation || initialSituation,
|
|
|
30 |
]
|
31 |
}
|
32 |
|
src/app/games/dungeon.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
import { amatic } from "@/lib/fonts"
|
2 |
-
import { Game
|
3 |
|
4 |
const actions = [
|
5 |
"not moving",
|
@@ -52,10 +52,11 @@ export const game: Game = {
|
|
52 |
initialSituation,
|
53 |
initialActionnables,
|
54 |
getScenePrompt: (situation?: string) => [
|
55 |
-
`screenshot from
|
56 |
// `first-person footage`,
|
57 |
-
|
58 |
-
`
|
59 |
`unreal engine`,
|
|
|
60 |
]
|
61 |
}
|
|
|
1 |
import { amatic } from "@/lib/fonts"
|
2 |
+
import { Game } from "./types"
|
3 |
|
4 |
const actions = [
|
5 |
"not moving",
|
|
|
52 |
initialSituation,
|
53 |
initialActionnables,
|
54 |
getScenePrompt: (situation?: string) => [
|
55 |
+
`screenshot from adventure videogame`,
|
56 |
// `first-person footage`,
|
57 |
+
`medieval dungeon`,
|
58 |
+
`adventure`,
|
59 |
`unreal engine`,
|
60 |
+
situation || initialSituation,
|
61 |
]
|
62 |
}
|
src/app/games/enchanters.ts
ADDED
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { macondo } from "@/lib/fonts"
|
2 |
+
import { Game } from "./types"
|
3 |
+
|
4 |
+
const initialSituation = [
|
5 |
+
`looking at a beautiful medieval castle on a lake, with a metallic gate, during golden house, surrounded by mountain, with a flying dragon visible afar`,
|
6 |
+
].join(", ")
|
7 |
+
|
8 |
+
const initialActionnables = [
|
9 |
+
"trees",
|
10 |
+
"dragon",
|
11 |
+
"castle",
|
12 |
+
"gate",
|
13 |
+
"stone wall",
|
14 |
+
"lake",
|
15 |
+
"roof"
|
16 |
+
]
|
17 |
+
|
18 |
+
export const game: Game = {
|
19 |
+
title: "Enchanters",
|
20 |
+
type: "enchanters",
|
21 |
+
className: macondo.className,
|
22 |
+
initialSituation,
|
23 |
+
initialActionnables,
|
24 |
+
getScenePrompt: (situation?: string) => [
|
25 |
+
`Screenshot from a videogame`,
|
26 |
+
`unreal engine`,
|
27 |
+
`magical wizard world`,
|
28 |
+
`first person`,
|
29 |
+
situation || initialSituation,
|
30 |
+
]
|
31 |
+
}
|
32 |
+
|
src/app/games/index.ts
CHANGED
@@ -4,9 +4,11 @@ import { game as pirates } from "./pirates"
|
|
4 |
import { game as city } from "./city"
|
5 |
import { game as dungeon } from "./dungeon"
|
6 |
import { game as doom } from "./doom"
|
|
|
|
|
7 |
|
8 |
-
export const games = { pirates, city, dungeon, doom }
|
9 |
|
10 |
-
export const defaultGame: GameType = "
|
11 |
|
12 |
export const getGame = (type?: GameType) => games[type || defaultGame] || games[defaultGame]
|
|
|
4 |
import { game as city } from "./city"
|
5 |
import { game as dungeon } from "./dungeon"
|
6 |
import { game as doom } from "./doom"
|
7 |
+
import { game as vernian } from "./vernian"
|
8 |
+
import { game as enchanters } from "./enchanters"
|
9 |
|
10 |
+
export const games = { pirates, city, dungeon, doom, vernian, enchanters}
|
11 |
|
12 |
+
export const defaultGame: GameType = "enchanters"
|
13 |
|
14 |
export const getGame = (type?: GameType) => games[type || defaultGame] || games[defaultGame]
|
src/app/games/pirates.ts
CHANGED
@@ -62,7 +62,10 @@ export const game: Game = {
|
|
62 |
// this prompt is beautiful:
|
63 |
// screenshot from an adventure videogame, inside the hold of a pirate ship, with a pirate chest in the center, at sunset, beautiful, award winning, unreal engine, intricate details
|
64 |
`screenshot from an adventure videogame`,
|
65 |
-
|
66 |
`unreal engine`,
|
|
|
|
|
|
|
67 |
],
|
68 |
}
|
|
|
62 |
// this prompt is beautiful:
|
63 |
// screenshot from an adventure videogame, inside the hold of a pirate ship, with a pirate chest in the center, at sunset, beautiful, award winning, unreal engine, intricate details
|
64 |
`screenshot from an adventure videogame`,
|
65 |
+
`pirate themed`,
|
66 |
`unreal engine`,
|
67 |
+
`pixar style`,
|
68 |
+
`goofy and comedical`,
|
69 |
+
situation || initialSituation,
|
70 |
],
|
71 |
}
|
src/app/games/types.ts
CHANGED
@@ -1,6 +1,5 @@
|
|
1 |
-
import { FontName } from "@/lib/fonts"
|
2 |
|
3 |
-
export type GameType =
|
4 |
|
5 |
export interface Scene {
|
6 |
actionnables: string[]
|
|
|
|
|
1 |
|
2 |
+
export type GameType = "pirates" | "city" | "dungeon" | "doom" | "vernian" | "enchanters"
|
3 |
|
4 |
export interface Scene {
|
5 |
actionnables: string[]
|
src/app/games/vernian.ts
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { imfell } from "@/lib/fonts"
|
2 |
+
import { Game } from "./types"
|
3 |
+
|
4 |
+
const initialSituation = [
|
5 |
+
`inside a secret workshop inspired by Jules Verne`,
|
6 |
+
`with mysterious machines, keys, boxes, blueprints, gears`
|
7 |
+
].join(", ")
|
8 |
+
|
9 |
+
const initialActionnables = [
|
10 |
+
"key",
|
11 |
+
"box",
|
12 |
+
"door",
|
13 |
+
"table",
|
14 |
+
"blueprint",
|
15 |
+
"gear",
|
16 |
+
"machine"
|
17 |
+
]
|
18 |
+
|
19 |
+
export const game: Game = {
|
20 |
+
title: "Vernian",
|
21 |
+
type: "vernian",
|
22 |
+
className: imfell.className,
|
23 |
+
initialSituation,
|
24 |
+
initialActionnables,
|
25 |
+
getScenePrompt: (situation?: string) => [
|
26 |
+
`Screenshot from a videogame`,
|
27 |
+
`steam punk`,
|
28 |
+
`jules verne architecture and design`,
|
29 |
+
`mysterious machines and mechanisms`,
|
30 |
+
`first person`,
|
31 |
+
`unreal engine`,
|
32 |
+
situation || initialSituation,
|
33 |
+
]
|
34 |
+
}
|
35 |
+
|
src/app/main.tsx
CHANGED
@@ -58,16 +58,18 @@ export default function Main() {
|
|
58 |
console.log("Rendering a scene for " + game.type)
|
59 |
|
60 |
// console.log(`rendering scene..`)
|
61 |
-
const newRendered = await render(
|
|
|
|
|
62 |
// SCENE PROMPT
|
63 |
-
game.getScenePrompt(nextSituation).join(", "),
|
64 |
|
65 |
// ACTIONNABLES
|
66 |
-
(Array.isArray(nextActionnables) && nextActionnables.length
|
67 |
? nextActionnables
|
68 |
: game.initialActionnables
|
69 |
).slice(0, 6) // too many can slow us down it seems
|
70 |
-
)
|
71 |
|
72 |
// detect if type game type changed while we were busy
|
73 |
if (game?.type !== gameRef?.current) {
|
@@ -153,8 +155,11 @@ export default function Main() {
|
|
153 |
router.replace(`${pathname}${query}`, { })
|
154 |
|
155 |
// workaround.. but it is strange that router.replace doesn't work..
|
156 |
-
let newurl = window.location.protocol + "//" + window.location.host + window.location.pathname + '?' + search.toString()
|
157 |
-
window.history.pushState({path: newurl}, '', newurl)
|
|
|
|
|
|
|
158 |
}
|
159 |
|
160 |
|
@@ -170,16 +175,19 @@ export default function Main() {
|
|
170 |
router.replace(`${pathname}${query}`, { })
|
171 |
|
172 |
// workaround.. but it is strange that router.replace doesn't work..
|
173 |
-
let newurl = window.location.protocol + "//" + window.location.host + window.location.pathname + '?' + search.toString()
|
174 |
-
window.history.pushState({path: newurl}, '', newurl)
|
|
|
|
|
|
|
175 |
}
|
176 |
|
177 |
return (
|
178 |
<div
|
179 |
-
className="flex flex-col w-full"
|
180 |
>
|
181 |
<div className="flex flex-row w-full justify-between items-center px-2 py-2 border-b-1 border-gray-50 bg-gray-800">
|
182 |
-
|
183 |
<label className="flex text-sm">Select a story:</label>
|
184 |
<Select
|
185 |
defaultValue={gameRef.current}
|
@@ -210,6 +218,7 @@ export default function Main() {
|
|
210 |
</Select>
|
211 |
</div>
|
212 |
</div>
|
|
|
213 |
<div className={[
|
214 |
"flex flex-col w-full pt-4 space-y-3 px-2",
|
215 |
getGame(gameRef.current).className // apply the game theme
|
@@ -223,17 +232,17 @@ export default function Main() {
|
|
223 |
{i < (clickables.length - 1) ? <div>,</div> : null}
|
224 |
</div>)}
|
225 |
</div>
|
226 |
-
<p className="text-xl
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
227 |
</div>
|
228 |
-
<Renderer
|
229 |
-
rendered={rendered}
|
230 |
-
onUserAction={handleUserAction}
|
231 |
-
onUserHover={setHoveredActionnable}
|
232 |
-
isLoading={isLoading}
|
233 |
-
game={game}
|
234 |
-
engine={engine}
|
235 |
-
/>
|
236 |
-
<p className="text-xl">{dialogue}</p>
|
237 |
</div>
|
238 |
)
|
239 |
}
|
|
|
58 |
console.log("Rendering a scene for " + game.type)
|
59 |
|
60 |
// console.log(`rendering scene..`)
|
61 |
+
const newRendered = await render({
|
62 |
+
engine,
|
63 |
+
|
64 |
// SCENE PROMPT
|
65 |
+
prompt: game.getScenePrompt(nextSituation).join(", "),
|
66 |
|
67 |
// ACTIONNABLES
|
68 |
+
actionnables: (Array.isArray(nextActionnables) && nextActionnables.length
|
69 |
? nextActionnables
|
70 |
: game.initialActionnables
|
71 |
).slice(0, 6) // too many can slow us down it seems
|
72 |
+
})
|
73 |
|
74 |
// detect if type game type changed while we were busy
|
75 |
if (game?.type !== gameRef?.current) {
|
|
|
155 |
router.replace(`${pathname}${query}`, { })
|
156 |
|
157 |
// workaround.. but it is strange that router.replace doesn't work..
|
158 |
+
//let newurl = window.location.protocol + "//" + window.location.host + window.location.pathname + '?' + search.toString()
|
159 |
+
//window.history.pushState({path: newurl}, '', newurl)
|
160 |
+
|
161 |
+
// actually we don't handle partial reload very well, so let's reload the whole page
|
162 |
+
window.location = `${window.location.protocol + "//" + window.location.host + window.location.pathname + '?' + search.toString()}` as any
|
163 |
}
|
164 |
|
165 |
|
|
|
175 |
router.replace(`${pathname}${query}`, { })
|
176 |
|
177 |
// workaround.. but it is strange that router.replace doesn't work..
|
178 |
+
// let newurl = window.location.protocol + "//" + window.location.host + window.location.pathname + '?' + search.toString()
|
179 |
+
// window.history.pushState({path: newurl}, '', newurl)
|
180 |
+
|
181 |
+
// actually we don't handle partial reload very well, so let's reload the whole page
|
182 |
+
window.location = `${window.location.protocol + "//" + window.location.host + window.location.pathname + '?' + search.toString()}` as any
|
183 |
}
|
184 |
|
185 |
return (
|
186 |
<div
|
187 |
+
className="flex flex-col w-full max-w-5xl"
|
188 |
>
|
189 |
<div className="flex flex-row w-full justify-between items-center px-2 py-2 border-b-1 border-gray-50 bg-gray-800">
|
190 |
+
<div className="flex flex-row items-center space-x-3 font-mono">
|
191 |
<label className="flex text-sm">Select a story:</label>
|
192 |
<Select
|
193 |
defaultValue={gameRef.current}
|
|
|
218 |
</Select>
|
219 |
</div>
|
220 |
</div>
|
221 |
+
|
222 |
<div className={[
|
223 |
"flex flex-col w-full pt-4 space-y-3 px-2",
|
224 |
getGame(gameRef.current).className // apply the game theme
|
|
|
232 |
{i < (clickables.length - 1) ? <div>,</div> : null}
|
233 |
</div>)}
|
234 |
</div>
|
235 |
+
<p className="text-xl">You are looking at: <span className="font-bold">{hoveredActionnable || "nothing"}</span></p>
|
236 |
+
<Renderer
|
237 |
+
rendered={rendered}
|
238 |
+
onUserAction={handleUserAction}
|
239 |
+
onUserHover={setHoveredActionnable}
|
240 |
+
isLoading={isLoading}
|
241 |
+
game={game}
|
242 |
+
engine={engine}
|
243 |
+
/>
|
244 |
+
<p className="text-xl">{dialogue}</p>
|
245 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
246 |
</div>
|
247 |
)
|
248 |
}
|
src/app/render.ts
CHANGED
@@ -3,6 +3,7 @@
|
|
3 |
import Gorgon from "@gorgonjs/gorgon"
|
4 |
|
5 |
import { RenderedScene } from "./types"
|
|
|
6 |
|
7 |
// note: there is no / at the end in the variable
|
8 |
// so we have to add it ourselves if needed
|
@@ -11,8 +12,29 @@ const apiUrl = process.env.RENDERING_ENGINE_API
|
|
11 |
|
12 |
const cacheDurationInSec = 30 * 60 // 30 minutes
|
13 |
|
14 |
-
export async function render(
|
15 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
let defaulResult: RenderedScene = {
|
17 |
assetUrl: "",
|
18 |
maskBase64: "",
|
@@ -32,7 +54,7 @@ export async function render(prompt: string, actionnables: string[] = []) {
|
|
32 |
body: JSON.stringify({
|
33 |
prompt,
|
34 |
// nbFrames: 8 and nbSteps: 15 --> ~10 sec generation
|
35 |
-
nbFrames
|
36 |
nbSteps: 20,
|
37 |
actionnables,
|
38 |
segmentation: "firstframe", // one day we will remove this param, to make it automatic
|
@@ -57,6 +79,7 @@ export async function render(prompt: string, actionnables: string[] = []) {
|
|
57 |
return response
|
58 |
} catch (err) {
|
59 |
console.error(err)
|
|
|
60 |
return defaulResult
|
61 |
}
|
62 |
}, cacheDurationInSec * 1000)
|
|
|
3 |
import Gorgon from "@gorgonjs/gorgon"
|
4 |
|
5 |
import { RenderedScene } from "./types"
|
6 |
+
import { Engine, EngineType } from "./engines"
|
7 |
|
8 |
// note: there is no / at the end in the variable
|
9 |
// so we have to add it ourselves if needed
|
|
|
12 |
|
13 |
const cacheDurationInSec = 30 * 60 // 30 minutes
|
14 |
|
15 |
+
export async function render({
|
16 |
+
prompt,
|
17 |
+
actionnables = [],
|
18 |
+
engine,
|
19 |
+
}: {
|
20 |
+
prompt: string
|
21 |
+
actionnables: string[]
|
22 |
+
engine: Engine
|
23 |
+
}) {
|
24 |
+
if (!prompt) {
|
25 |
+
console.error(`cannot call the rendering API without a prompt, aborting..`)
|
26 |
+
throw new Error(`cannot call the rendering API without a prompt, aborting..`)
|
27 |
+
}
|
28 |
+
if (!Array.isArray(actionnables) || !actionnables.length) {
|
29 |
+
console.error(`cannot call the rendering API without actionnables, aborting..`)
|
30 |
+
throw new Error(`cannot call the rendering API without actionnables, aborting..`)
|
31 |
+
}
|
32 |
+
|
33 |
+
const nbFrames = engine.type === "video" ? 8 : 1
|
34 |
+
|
35 |
+
const cacheKey = `render/${JSON.stringify({ prompt, actionnables, nbFrames, type: engine.type })}`
|
36 |
+
|
37 |
+
return await Gorgon.get(cacheKey, async () => {
|
38 |
let defaulResult: RenderedScene = {
|
39 |
assetUrl: "",
|
40 |
maskBase64: "",
|
|
|
54 |
body: JSON.stringify({
|
55 |
prompt,
|
56 |
// nbFrames: 8 and nbSteps: 15 --> ~10 sec generation
|
57 |
+
nbFrames, // when nbFrames is 1, we will only generate static images
|
58 |
nbSteps: 20,
|
59 |
actionnables,
|
60 |
segmentation: "firstframe", // one day we will remove this param, to make it automatic
|
|
|
79 |
return response
|
80 |
} catch (err) {
|
81 |
console.error(err)
|
82 |
+
Gorgon.clear(cacheKey)
|
83 |
return defaulResult
|
84 |
}
|
85 |
}, cacheDurationInSec * 1000)
|
src/components/business/renderer.tsx
CHANGED
@@ -213,12 +213,12 @@ export const Renderer = ({
|
|
213 |
|
214 |
return (
|
215 |
<div className={[
|
216 |
-
"w-full py-8
|
217 |
// isLoading ? "animate-pulse" : ""
|
218 |
].join(" ")
|
219 |
}>
|
220 |
<div className="relative w-[1024px] h-[512px] border-2 border-gray-50 rounded-xl overflow-hidden">
|
221 |
-
{assetUrl
|
222 |
null
|
223 |
: engine.type === "video"
|
224 |
? <video
|
|
|
213 |
|
214 |
return (
|
215 |
<div className={[
|
216 |
+
"w-full py-8",
|
217 |
// isLoading ? "animate-pulse" : ""
|
218 |
].join(" ")
|
219 |
}>
|
220 |
<div className="relative w-[1024px] h-[512px] border-2 border-gray-50 rounded-xl overflow-hidden">
|
221 |
+
{!assetUrl ?
|
222 |
null
|
223 |
: engine.type === "video"
|
224 |
? <video
|
src/lib/fonts.ts
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
import { Inter, Edu_SA_Beginner, Orbitron, Amatic_SC } from "next/font/google"
|
2 |
import localFont from "next/font/local"
|
3 |
|
4 |
export const inter = Inter({
|
@@ -22,6 +22,18 @@ export const amatic = Amatic_SC({
|
|
22 |
variable: "--font-amatic"
|
23 |
})
|
24 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
25 |
export const lugrasimo = localFont({
|
26 |
src: "../fonts/Lugrasimo-Regular.woff2",
|
27 |
variable: "--font-lugrasimo"
|
@@ -39,6 +51,7 @@ export const fontList = [
|
|
39 |
edu,
|
40 |
orbitron,
|
41 |
amatic,
|
|
|
42 |
lugrasimo,
|
43 |
]
|
44 |
|
@@ -46,4 +59,4 @@ export const classNames = fontList.map(font => font.className)
|
|
46 |
|
47 |
export const className = classNames.join(" ")
|
48 |
|
49 |
-
export type FontName = "font-inter" | "font-sans" | "font-edu" | "font-orbitron" | "font-amatic" | "font-lugrasimo"
|
|
|
1 |
+
import { Inter, Edu_SA_Beginner, Orbitron, Amatic_SC, Macondo_Swash_Caps, IM_Fell_English_SC } from "next/font/google"
|
2 |
import localFont from "next/font/local"
|
3 |
|
4 |
export const inter = Inter({
|
|
|
22 |
variable: "--font-amatic"
|
23 |
})
|
24 |
|
25 |
+
export const macondo = Macondo_Swash_Caps({
|
26 |
+
subsets: ["latin"],
|
27 |
+
weight: "400",
|
28 |
+
variable: "--font-macondo"
|
29 |
+
})
|
30 |
+
|
31 |
+
export const imfell = IM_Fell_English_SC({
|
32 |
+
subsets: ["latin"],
|
33 |
+
weight: "400",
|
34 |
+
variable: "--font-imfell"
|
35 |
+
})
|
36 |
+
|
37 |
export const lugrasimo = localFont({
|
38 |
src: "../fonts/Lugrasimo-Regular.woff2",
|
39 |
variable: "--font-lugrasimo"
|
|
|
51 |
edu,
|
52 |
orbitron,
|
53 |
amatic,
|
54 |
+
macondo,
|
55 |
lugrasimo,
|
56 |
]
|
57 |
|
|
|
59 |
|
60 |
export const className = classNames.join(" ")
|
61 |
|
62 |
+
export type FontName = "font-inter" | "font-sans" | "font-edu" | "font-orbitron" | "font-amatic" | "font-macondo" | "font-lugrasimo"
|