Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ such as `character.player` or `env.ground`.
"object:TankConfiguration::CombinedTank/TankBase": "vehicle.tank.base"
},
"effects": {
"effect:Game Scene//SkyBox": { "frontFaceResourceName": "sky.day.front" }
"effect:Game Scene//SkyBox": "sky.day"
},
"ignoredObjects": ["scene:Game Scene/Camera"]
}
Expand All @@ -66,8 +66,9 @@ such as `character.player` or `env.ground`.
An object is referred to as `scene:<scene>/<object>`, `global/<object>`, or
`object:<Extension>::<CustomObject>/<child>` for the children of a custom
object. A 3D model maps to one model slot. A 3D cube maps either to one texture
slot for all its faces, or to one slot per face. A skybox effect maps each of
its texture parameters.
slot for all its faces, or to one slot per face. A skybox effect, referred to
as `effect:<scene>/<layer>/<effect>`, maps to one skybox slot: the theme's
skybox replaces its six faces.

**When you add a 3D starter, add its `theme-slots.json`.** The build refuses to
publish a 3D starter without one, or one where a 3D model or cube is neither
Expand Down
11 changes: 2 additions & 9 deletions examples/starting-3D-platformer/theme-slots.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,6 @@
}
},
"effects": {
"effect:Game Scene//SkyBox": {
"backFaceResourceName": "sky.day.back",
"bottomFaceResourceName": "sky.day.bottom",
"frontFaceResourceName": "sky.day.front",
"leftFaceResourceName": "sky.day.left",
"rightFaceResourceName": "sky.day.right",
"topFaceResourceName": "sky.day.top"
}
"effect:Game Scene//SkyBox": "sky.day"
}
}
}
11 changes: 2 additions & 9 deletions examples/starting-3d-car-racing/theme-slots.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,6 @@
"scene:Game Scene/Walls_3D": "env.wall"
},
"effects": {
"effect:Game Scene//SkyBox": {
"backFaceResourceName": "sky.day.back",
"bottomFaceResourceName": "sky.day.bottom",
"frontFaceResourceName": "sky.day.front",
"leftFaceResourceName": "sky.day.left",
"rightFaceResourceName": "sky.day.right",
"topFaceResourceName": "sky.day.top"
}
"effect:Game Scene//SkyBox": "sky.day"
}
}
}
11 changes: 2 additions & 9 deletions examples/starting-3d-driving/theme-slots.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,6 @@
"scene:Game Scene/Walls_3D": "env.wall"
},
"effects": {
"effect:Game Scene//SkyBox": {
"backFaceResourceName": "sky.day.back",
"bottomFaceResourceName": "sky.day.bottom",
"frontFaceResourceName": "sky.day.front",
"leftFaceResourceName": "sky.day.left",
"rightFaceResourceName": "sky.day.right",
"topFaceResourceName": "sky.day.top"
}
"effect:Game Scene//SkyBox": "sky.day"
}
}
}
11 changes: 2 additions & 9 deletions examples/starting-3d-flight-sim/theme-slots.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,6 @@
"scene:Game Scene/PlayerAircraft": "vehicle.aircraft.player"
},
"effects": {
"effect:Game Scene//SkyBox": {
"backFaceResourceName": "sky.day.back",
"bottomFaceResourceName": "sky.day.bottom",
"frontFaceResourceName": "sky.day.front",
"leftFaceResourceName": "sky.day.left",
"rightFaceResourceName": "sky.day.right",
"topFaceResourceName": "sky.day.top"
}
"effect:Game Scene//SkyBox": "sky.day"
}
}
}
11 changes: 2 additions & 9 deletions examples/starting-3d-tank/theme-slots.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,6 @@
"object:TankConfiguration::TankTop/TankCanon": "vehicle.tank.cannon"
},
"effects": {
"effect:Game Scene//SkyBox": {
"backFaceResourceName": "sky.day.back",
"bottomFaceResourceName": "sky.day.bottom",
"frontFaceResourceName": "sky.day.front",
"leftFaceResourceName": "sky.day.left",
"rightFaceResourceName": "sky.day.right",
"topFaceResourceName": "sky.day.top"
}
"effect:Game Scene//SkyBox": "sky.day"
}
}
}
9 changes: 1 addition & 8 deletions examples/starting-first-person-farming/theme-slots.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,7 @@
}
},
"effects": {
"effect:Game Scene//Effect2": {
"backFaceResourceName": "sky.day.back",
"bottomFaceResourceName": "sky.day.bottom",
"frontFaceResourceName": "sky.day.front",
"leftFaceResourceName": "sky.day.left",
"rightFaceResourceName": "sky.day.right",
"topFaceResourceName": "sky.day.top"
}
"effect:Game Scene//Effect2": "sky.day"
},
"ignoredObjects": [
"scene:Game Scene/Player"
Expand Down
9 changes: 1 addition & 8 deletions examples/starting-first-person-horror/theme-slots.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,7 @@
"scene:Game Scene/Monster": "character.monster"
},
"effects": {
"effect:Game Scene//SkyBox": {
"backFaceResourceName": "sky.dark.back",
"bottomFaceResourceName": "sky.dark.bottom",
"frontFaceResourceName": "sky.dark.front",
"leftFaceResourceName": "sky.dark.left",
"rightFaceResourceName": "sky.dark.right",
"topFaceResourceName": "sky.dark.top"
}
"effect:Game Scene//SkyBox": "sky.dark"
},
"ignoredObjects": [
"scene:Game Scene/PathfindingObstacle",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -876,7 +876,7 @@
"locked": true,
"name": "Gun",
"persistentUuid": "84abff0d-54b1-41a2-b557-c1442e215f5a",
"rotationX": 0,
"rotationX": 180,
"rotationY": 90,
"width": 0,
"x": 1156,
Expand Down Expand Up @@ -1671,8 +1671,8 @@
"modelResourceName": "assets\\StartingGun_orange.glb",
"originLocation": "ModelOrigin",
"rotationX": 0,
"rotationY": 180,
"rotationZ": 0,
"rotationY": 0,
"rotationZ": 180,
"width": 2252.6,
"animations": []
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,7 @@
"scene:Game Scene/Gun": "weapon.handheld"
},
"effects": {
"effect:Game Scene//SkyBox": {
"backFaceResourceName": "sky.dark.back",
"bottomFaceResourceName": "sky.dark.bottom",
"frontFaceResourceName": "sky.dark.front",
"leftFaceResourceName": "sky.dark.left",
"rightFaceResourceName": "sky.dark.right",
"topFaceResourceName": "sky.dark.top"
}
"effect:Game Scene//SkyBox": "sky.dark"
},
"ignoredObjects": [
"scene:Game Scene/PathfindingObstacle",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -555,7 +555,7 @@
"locked": true,
"name": "Gun",
"persistentUuid": "701527b6-1157-47a2-bd12-4e6ccb24835e",
"rotationX": 0,
"rotationX": 180,
"rotationY": 90,
"width": 0,
"x": 1156,
Expand Down Expand Up @@ -1406,8 +1406,8 @@
"modelResourceName": "assets\\StartingGun_orange.glb",
"originLocation": "ModelOrigin",
"rotationX": 0,
"rotationY": 180,
"rotationZ": 0,
"rotationY": 0,
"rotationZ": 180,
"width": 2252.64,
"animations": []
}
Expand Down
9 changes: 1 addition & 8 deletions examples/starting-first-person-shooter/theme-slots.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,7 @@
"scene:Game Scene/Target": "character.player"
},
"effects": {
"effect:Game Scene//SkyBox": {
"backFaceResourceName": "sky.day.back",
"bottomFaceResourceName": "sky.day.bottom",
"frontFaceResourceName": "sky.day.front",
"leftFaceResourceName": "sky.day.left",
"rightFaceResourceName": "sky.day.right",
"topFaceResourceName": "sky.day.top"
}
"effect:Game Scene//SkyBox": "sky.day"
},
"ignoredObjects": [
"scene:Game Scene/Player"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,7 @@
"scene:Game Scene/Harvest_Tree": "nature.tree"
},
"effects": {
"effect:Game Scene//SkyBox": {
"backFaceResourceName": "sky.day.back",
"bottomFaceResourceName": "sky.day.bottom",
"frontFaceResourceName": "sky.day.front",
"leftFaceResourceName": "sky.day.left",
"rightFaceResourceName": "sky.day.right",
"topFaceResourceName": "sky.day.top"
}
"effect:Game Scene//SkyBox": "sky.day"
},
"ignoredObjects": [
"scene:Game Scene/Player"
Expand Down
9 changes: 1 addition & 8 deletions examples/starting-first-person/theme-slots.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,7 @@
"scene:Game Scene/PushableBox": "env.crate"
},
"effects": {
"effect:Game Scene//SkyBox": {
"backFaceResourceName": "sky.day.back",
"bottomFaceResourceName": "sky.day.bottom",
"frontFaceResourceName": "sky.day.front",
"leftFaceResourceName": "sky.day.left",
"rightFaceResourceName": "sky.day.right",
"topFaceResourceName": "sky.day.top"
}
"effect:Game Scene//SkyBox": "sky.day"
},
"ignoredObjects": [
"scene:Game Scene/Player"
Expand Down
76 changes: 49 additions & 27 deletions scripts/lib/StarterThemer.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,22 @@

/**
* A theme, as published by the assets repository: for each slot, the file to
* use and, for a 3D model, the serialized content of the asset's object.
* use and, for a 3D model, the serialized content of the asset's object. A
* skybox slot has one file per face instead.
* @typedef {{
* kind: 'model' | 'texture',
* file: string,
* resourceName: string,
* origin?: {name: string, identifier: string},
* }} ThemeFile
*
* @typedef {ThemeFile & {
* kind: 'model' | 'texture',
* objectContent?: any,
* assetStoreId?: string,
* origin?: {name: string, identifier: string},
* } | {
* kind: 'skybox',
* faces: Object.<string, ThemeFile>,
* assetStoreId?: string,
* }} ThemeSlot
*
* @typedef {{
Expand Down Expand Up @@ -147,8 +155,8 @@ const getSizeRatio = (objectContent, themeContent) => {
* The starter's theme-slots.json says which of its objects play which slot.
* Each such object gets the theme's asset for that slot: a 3D model takes the
* theme model's dimensions, rotation, material and animations while keeping
* its name, behaviors, variables, instances, origin and center; a cube face or
* a skybox face points at the theme image.
* its name, behaviors, variables, instances, origin and center; a cube face
* points at the theme image; a skybox effect points at the six theme images.
*
* Each theme file becomes a resource named like in the asset store, and the
* resources of the starter that nothing uses anymore are removed.
Expand Down Expand Up @@ -179,39 +187,36 @@ const applyThemeToStarter = (projectObject, starterThemeSlots, theme) => {
let changedObjectsCount = 0;

/**
* Add the resource of a slot to the project, configured like the resource it
* replaces, and return its name.
* Add a file of a slot to the project as a resource configured like the
* resource it replaces, and return its name.
* @param {string} replacedResourceName
* @param {string} slotId
* @param {ThemeFile} themeFile
* @returns {string | null}
*/
const useThemeResource = (replacedResourceName, slotId) => {
const slot = theme.slots[slotId];
if (!slot) {
missingSlots.add(slotId);
return null;
}
const useThemeResource = (replacedResourceName, slotId, themeFile) => {
const replacedResource = resourceByName.get(replacedResourceName);
if (!replacedResource) return null;

// A resource of the starter can already have that name: number the new one.
let name = slot.resourceName;
let name = themeFile.resourceName;
for (
let index = 2;
resourceByName.has(name) && resourceByName.get(name).file !== slot.file;
resourceByName.has(name) &&
resourceByName.get(name).file !== themeFile.file;
index++
) {
name = `${slot.resourceName} ${index}`;
name = `${themeFile.resourceName} ${index}`;
}

if (!resourceByName.has(name)) {
const themeResource = {
...replacedResource,
name,
file: slot.file,
origin: slot.origin || {
file: themeFile.file,
origin: themeFile.origin || {
name: 'gdevelop-asset-store',
identifier: slot.file,
identifier: themeFile.file,
},
};
resources.push(themeResource);
Expand Down Expand Up @@ -239,7 +244,8 @@ const applyThemeToStarter = (projectObject, starterThemeSlots, theme) => {
}
const modelResourceName = useThemeResource(
objectContent.modelResourceName,
mapping
mapping,
slot
);
if (!modelResourceName) return;

Expand Down Expand Up @@ -272,7 +278,16 @@ const applyThemeToStarter = (projectObject, starterThemeSlots, theme) => {
const faceProperty = cubeFaceProperties[face];
const resourceName = objectContent[faceProperty];
if (!slotId || !resourceName) return;
const themeResourceName = useThemeResource(resourceName, slotId);
const slot = theme.slots[slotId];
if (!slot || slot.kind !== 'texture') {
missingSlots.add(slotId);
return;
}
const themeResourceName = useThemeResource(
resourceName,
slotId,
slot
);
if (!themeResourceName) return;
objectContent[faceProperty] = themeResourceName;
changed = true;
Expand All @@ -283,18 +298,25 @@ const applyThemeToStarter = (projectObject, starterThemeSlots, theme) => {
);

Object.entries(starterThemeSlots.effects || {}).forEach(
([effectPath, parameters]) => {
([effectPath, slotId]) => {
const effect = effectsByPath.get(effectPath);
if (!effect || !effect.stringParameters) return;
Object.entries(parameters).forEach(([parameter, slotId]) => {
const resourceName = effect.stringParameters[parameter];
if (!resourceName) return;
const slot = theme.slots[slotId];
if (!slot || slot.kind !== 'skybox') {
missingSlots.add(slotId);
return;
}
Object.entries(cubeFaceProperties).forEach(([face, faceProperty]) => {
const resourceName = effect.stringParameters[faceProperty];
const themeFile = slot.faces[face];
if (!resourceName || !themeFile) return;
const themeResourceName = useThemeResource(
String(resourceName),
slotId
slotId,
themeFile
);
if (themeResourceName) {
effect.stringParameters[parameter] = themeResourceName;
effect.stringParameters[faceProperty] = themeResourceName;
}
});
}
Expand Down
Loading
Loading