Add support for model vertex colors (#3346)

* Add model vertex color support

* Fix an issue with the Tiangong model not loading properly and set a correct bounding sphere size for it

* Update caching for models

* Update previous model examples to the new format

* And add a new example to test the new vertex colors support

* Apply suggestions from code review

Co-authored-by: Alexander Bock <alexander.bock@liu.se>

* Add a model example with lighting

* Improve the basic example and add a separate example for vertex colors

* Add visual test for vertex colors (#3348)

* Update the readme file for the visual tests

* Add asset instruction to the visual testing readme

* Add visual test for RenderableModel with vertex colors

* Apply suggestions from code review

Co-authored-by: Alexander Bock <alexander.bock@liu.se>

* Update test when asset changed name

---------

Co-authored-by: Alexander Bock <alexander.bock@liu.se>

* Update Ghoul

---------

Co-authored-by: Alexander Bock <alexander.bock@liu.se>
This commit is contained in:
Malin E
2024-07-17 09:56:06 -04:00
committed by GitHub
parent 6337b8e5aa
commit 01483ef982
19 changed files with 509 additions and 269 deletions
-196
View File
@@ -1,196 +0,0 @@
local sun = asset.require("scene/solarsystem/sun/transforms")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local model = asset.resource({
Name = "Animated Box",
Type = "HttpSynchronization",
Identifier = "animated_box",
Version = 1
})
local StartTime = "2021 06 01 00:00:00"
local AnimationLoop = {
Identifier = "AnimationLoop",
Parent = transforms.EarthCenter.Identifier,
Transform = {
Translation = {
Type = "StaticTranslation",
Position = { 0.0, -11E7, 0.0 }
}
},
Renderable = {
Type = "RenderableModel",
GeometryFile = model .. "BoxAnimated.glb",
EnableAnimation = true,
AnimationMode = "LoopFromStart",
AnimationStartTime = StartTime,
ModelScale = 3E7,
LightSources = {
sun.LightSource
}
},
GUI = {
Name = "Animated Model (LoopFromStart)",
Path = "/Examples",
Description = "Simple animated box model with the animation mode 'LoopFromStart'"
}
}
local AnimationLoopInf = {
Identifier = "AnimationLoopInf",
Parent = transforms.EarthCenter.Identifier,
Transform = {
Translation = {
Type = "StaticTranslation",
Position = { 0.0, 11E7, 0.0 }
}
},
Renderable = {
Type = "RenderableModel",
GeometryFile = model .. "BoxAnimated.glb",
EnableAnimation = true,
AnimationMode = "LoopInfinitely",
AnimationStartTime = StartTime,
ModelScale = 3E7,
LightSources = {
sun.LightSource
}
},
GUI = {
Name = "Animated Model (LoopInfinitely)",
Path = "/Examples",
Description = "Simple animated box model with the animation mode 'LoopInfinitely'"
}
}
local AnimationOnce = {
Identifier = "AnimationOnce",
Parent = transforms.EarthCenter.Identifier,
Transform = {
Translation = {
Type = "StaticTranslation",
Position = { 11E7, 0.0, 0.0 }
}
},
Renderable = {
Type = "RenderableModel",
GeometryFile = model .. "BoxAnimated.glb",
EnableAnimation = true,
AnimationMode = "Once",
AnimationStartTime = StartTime,
ModelScale = 3E7,
LightSources = {
sun.LightSource
}
},
GUI = {
Name = "Animated Model (Once)",
Path = "/Examples",
Description = "Simple animated box model with the animation mode 'Once'"
}
}
local AnimationBounceInf = {
Identifier = "AnimationBounceInf",
Parent = transforms.EarthCenter.Identifier,
Transform = {
Translation = {
Type = "StaticTranslation",
Position = { 0.0, 0.0, 11E7 }
}
},
Renderable = {
Type = "RenderableModel",
GeometryFile = model .. "BoxAnimated.glb",
EnableAnimation = true,
AnimationMode = "BounceInfinitely",
AnimationStartTime = StartTime,
ModelScale = 3E7,
LightSources = {
sun.LightSource
}
},
GUI = {
Name = "Animated Model (BounceInfinitely)",
Path = "/Examples",
Description = "Simple animated box model with the animation mode 'BounceInfinitely'"
}
}
local AnimationBounce = {
Identifier = "AnimationBounce",
Parent = transforms.EarthCenter.Identifier,
Transform = {
Translation = {
Type = "StaticTranslation",
Position = { 0.0, 0.0, -11E7 }
}
},
Renderable = {
Type = "RenderableModel",
GeometryFile = model .. "BoxAnimated.glb",
EnableAnimation = true,
AnimationMode = "BounceFromStart",
AnimationStartTime = StartTime,
ModelScale = 3E7,
LightSources = {
sun.LightSource
}
},
GUI = {
Name = "Animated Model (BounceFromStart)",
Path = "/Examples",
Description = "Simple animated box model with the animation mode 'BounceFromStart'"
}
}
asset.onInitialize(function()
openspace.addSceneGraphNode(AnimationLoop)
openspace.addSceneGraphNode(AnimationLoopInf)
openspace.addSceneGraphNode(AnimationOnce)
openspace.addSceneGraphNode(AnimationBounceInf)
openspace.addSceneGraphNode(AnimationBounce)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(AnimationBounce)
openspace.removeSceneGraphNode(AnimationBounceInf)
openspace.removeSceneGraphNode(AnimationOnce)
openspace.removeSceneGraphNode(AnimationLoopInf)
openspace.removeSceneGraphNode(AnimationLoop)
end)
asset.export(AnimationLoop)
asset.export(AnimationLoopInf)
asset.export(AnimationOnce)
asset.export(AnimationBounceInf)
asset.export(AnimationBounce)
asset.meta = {
Name = "Animation Example asset",
Description = "Simple animation example asset with an animated box model",
Author = "OpenSpace Team",
URL = "http://openspaceproject.com",
License = "MIT license"
}
-- Model
-- @TODO: At the moment, this overwrites the previous meta description. Probably needs a way to specify multiple meta's per file?
asset.meta = {
Name = "Animated Box Model",
Description = "Simple animated box model",
Author = "Cesium, https://cesium.com/",
URL = "https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/BoxAnimated",
License = [[
Creative Commons Attribution 4.0 International License,
https://creativecommons.org/licenses/by/4.0/
]]
}
@@ -1,53 +0,0 @@
local sun = asset.require("scene/solarsystem/sun/sun")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local model = asset.resource({
Name = "Animated Box",
Type = "HttpSynchronization",
Identifier = "animated_box",
Version = 1
})
local Model = {
Identifier = "ModelShader",
Parent = transforms.EarthCenter.Identifier,
Transform = {
Translation = {
Type = "StaticTranslation",
Position = { -11E7, 0.0, 0.0 }
}
},
Renderable = {
Type = "RenderableModel",
GeometryFile = model .. "BoxAnimated.glb",
ModelScale = 3E7,
-- (malej 2023-MAY-22) Note that PerformShading should be false in this example,
-- since these example shaders dont't contain any light calculations
PerformShading = false,
VertexShader = asset.resource("model_vs.glsl"),
FragmentShader = asset.resource("model_fs.glsl"),
EnableAnimation = true,
AnimationStartTime = "2023 05 11 00:00:00",
AnimationTimeScale = "Second",
AnimationMode = "LoopInfinitely"
},
GUI = {
Name = "Model Shader",
Path = "/Examples",
Description = "Simple box model with a custom shader"
}
}
asset.onInitialize(function()
openspace.addSceneGraphNode(Model)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(Model)
end)
asset.export(Model)
@@ -0,0 +1,50 @@
-- Custom Shaders
-- This example loads a model with custom shaders.
-- Load the example model from OpenSpace servers
-- If you want to use your own model, this block of code can be safely deleted
local model = asset.resource({
Name = "Animated Box",
Type = "HttpSynchronization",
Identifier = "animated_box",
Version = 1
})
local Node = {
Identifier = "RenderableModel_Example_Shader",
Renderable = {
Type = "RenderableModel",
GeometryFile = model .. "BoxAnimated.glb",
-- Use the line below insted of the one above if you want to use your own model
--GeometryFile = "C:/path/to/model.fbx",
-- PerformShading is turned off since the provided custom shaders does not do any
-- light calculations
PerformShading = false,
-- Custom shaders
VertexShader = asset.resource("../data/model_vs.glsl"),
FragmentShader = asset.resource("../data/model_fs.glsl"),
},
GUI = {
Name = "RenderableModel - Custom Shaders",
Path = "/Examples"
}
}
asset.onInitialize(function()
openspace.addSceneGraphNode(Node)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(Node)
end)
-- Model credit
--[[
Author = Cesium, https://cesium.com/
URL = https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/BoxAnimated
License =
Creative Commons Attribution 4.0 International License,
https://creativecommons.org/licenses/by/4.0/
]]
@@ -0,0 +1,43 @@
-- Basic
-- This example loads a model.
-- Load the example model from OpenSpace servers
-- If you want to use your own model, this block of code can be safely deleted
local model = asset.resource({
Name = "Animated Box",
Type = "HttpSynchronization",
Identifier = "animated_box",
Version = 1
})
local Node = {
Identifier = "RenderableModel_Example",
Renderable = {
Type = "RenderableModel",
GeometryFile = model .. "BoxAnimated.glb",
-- Use the line below insted of the one above if you want to use your own model
--GeometryFile = "C:/path/to/model.fbx",
},
GUI = {
Name = "RenderableModel - Basic",
Path = "/Examples"
}
}
asset.onInitialize(function()
openspace.addSceneGraphNode(Node)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(Node)
end)
-- Model credit
--[[
Author = Cesium, https://cesium.com/
URL = https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/BoxAnimated
License =
Creative Commons Attribution 4.0 International License,
https://creativecommons.org/licenses/by/4.0/
]]
@@ -0,0 +1,49 @@
-- Animation
-- This example loads a model with an animation. The animation starts at a set time, in
-- this case "2024 07 09 12:00:00".
-- Load the example model from OpenSpace servers
-- If you want to use your own model, this block of code can be safely deleted
local model = asset.resource({
Name = "Animated Box",
Type = "HttpSynchronization",
Identifier = "animated_box",
Version = 1
})
local Node = {
Identifier = "RenderableModel_Example_Animation",
Renderable = {
Type = "RenderableModel",
GeometryFile = model .. "BoxAnimated.glb",
-- Use the line below insted of the one above if you want to use your own model
--GeometryFile = "C:/path/to/model.fbx",
-- Animation Parameters:
EnableAnimation = true,
-- Start the animation and play it once at this time
AnimationStartTime = "2024 07 09 12:00:00",
},
GUI = {
Name = "RenderableModel - Basic Animation",
Path = "/Examples"
}
}
asset.onInitialize(function()
openspace.addSceneGraphNode(Node)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(Node)
end)
-- Model credit
--[[
Author = Cesium, https://cesium.com/
URL = https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/BoxAnimated
License =
Creative Commons Attribution 4.0 International License,
https://creativecommons.org/licenses/by/4.0/
]]
@@ -0,0 +1,52 @@
-- Animation Bounce From Start
-- This example loads a model with an animation. The animation starts at a set time, in
-- this case "2024 07 09 12:00:00" and is set to bounce after that time (bounce is similar
-- to a boomerang for videos).
-- Load the example model from OpenSpace servers
-- If you want to use your own model, this block of code can be safely deleted
local model = asset.resource({
Name = "Animated Box",
Type = "HttpSynchronization",
Identifier = "animated_box",
Version = 1
})
local Node = {
Identifier = "RenderableModel_Example_Animation_Bounce",
Renderable = {
Type = "RenderableModel",
GeometryFile = model .. "BoxAnimated.glb",
-- Use the line below insted of the one above if you want to use your own model
--GeometryFile = "C:/path/to/model.fbx",
-- Animation Parameters:
EnableAnimation = true,
-- Start the animation and play it once at this time
AnimationStartTime = "2024 07 09 12:00:00",
-- Bounce the animation after the set start time
AnimationMode = "BounceFromStart",
},
GUI = {
Name = "RenderableModel - Animation Bounce From Start",
Path = "/Examples"
}
}
asset.onInitialize(function()
openspace.addSceneGraphNode(Node)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(Node)
end)
-- Model credit
--[[
Author = Cesium, https://cesium.com/
URL = https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/BoxAnimated
License =
Creative Commons Attribution 4.0 International License,
https://creativecommons.org/licenses/by/4.0/
]]
@@ -0,0 +1,52 @@
-- Animation Bounce Infinitely
-- This example loads a model with an animation. The animation starts at a set time, in
-- this case "2024 07 09 12:00:00" and is set to bounce both before and after that time
-- (bounce is similar to a boomerang for videos).
-- Load the example model from OpenSpace servers
-- If you want to use your own model, this block of code can be safely deleted
local model = asset.resource({
Name = "Animated Box",
Type = "HttpSynchronization",
Identifier = "animated_box",
Version = 1
})
local Node = {
Identifier = "RenderableModel_Example_Animation_Bounce_Infinitely",
Renderable = {
Type = "RenderableModel",
GeometryFile = model .. "BoxAnimated.glb",
-- Use the line below insted of the one above if you want to use your own model
--GeometryFile = "C:/path/to/model.fbx",
-- Animation Parameters:
EnableAnimation = true,
-- Start the animation and play it once at this time
AnimationStartTime = "2024 07 09 12:00:00",
-- Bounce the animation both before and after the set start time
AnimationMode = "BounceInfinitely",
},
GUI = {
Name = "RenderableModel - Animation Bounce Infinitely",
Path = "/Examples"
}
}
asset.onInitialize(function()
openspace.addSceneGraphNode(Node)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(Node)
end)
-- Model credit
--[[
Author = Cesium, https://cesium.com/
URL = https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/BoxAnimated
License =
Creative Commons Attribution 4.0 International License,
https://creativecommons.org/licenses/by/4.0/
]]
@@ -0,0 +1,51 @@
-- Animation Loop From Start
-- This example loads a model with an animation. The animation starts at a set time, in
-- this case "2024 07 09 12:00:00" and is set to loop after that time.
-- Load the example model from OpenSpace servers
-- If you want to use your own model, this block of code can be safely deleted
local model = asset.resource({
Name = "Animated Box",
Type = "HttpSynchronization",
Identifier = "animated_box",
Version = 1
})
local Node = {
Identifier = "RenderableModel_Example_Animation_Loop",
Renderable = {
Type = "RenderableModel",
GeometryFile = model .. "BoxAnimated.glb",
-- Use the line below insted of the one above if you want to use your own model
--GeometryFile = "C:/path/to/model.fbx",
-- Animation Parameters:
EnableAnimation = true,
-- Start the animation and play it once at this time
AnimationStartTime = "2024 07 09 12:00:00",
-- Loop the animation after the set start time
AnimationMode = "LoopFromStart",
},
GUI = {
Name = "RenderableModel - Animation Loop From Start",
Path = "/Examples"
}
}
asset.onInitialize(function()
openspace.addSceneGraphNode(Node)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(Node)
end)
-- Model credit
--[[
Author = Cesium, https://cesium.com/
URL = https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/BoxAnimated
License =
Creative Commons Attribution 4.0 International License,
https://creativecommons.org/licenses/by/4.0/
]]
@@ -0,0 +1,51 @@
-- Animation Loop Infinitely
-- This example loads a model with an animation. The animation starts at a set time, in
-- this case "2024 07 09 12:00:00" and is set to loop both before and after that time.
-- Load the example model from OpenSpace servers
-- If you want to use your own model, this block of code can be safely deleted
local model = asset.resource({
Name = "Animated Box",
Type = "HttpSynchronization",
Identifier = "animated_box",
Version = 1
})
local Node = {
Identifier = "RenderableModel_Example_Animation_Loop_Infinitely",
Renderable = {
Type = "RenderableModel",
GeometryFile = model .. "BoxAnimated.glb",
-- Use the line below insted of the one above if you want to use your own model
--GeometryFile = "C:/path/to/model.fbx",
-- Animation Parameters:
EnableAnimation = true,
-- Start the animation and play it once at this time
AnimationStartTime = "2024 07 09 12:00:00",
-- Loop the animation both before and after the set start time
AnimationMode = "LoopInfinitely",
},
GUI = {
Name = "RenderableModel - Animation Loop Infinitely",
Path = "/Examples"
}
}
asset.onInitialize(function()
openspace.addSceneGraphNode(Node)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(Node)
end)
-- Model credit
--[[
Author = Cesium, https://cesium.com/
URL = https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/BoxAnimated
License =
Creative Commons Attribution 4.0 International License,
https://creativecommons.org/licenses/by/4.0/
]]
@@ -0,0 +1,51 @@
-- Lighting
-- This example loads a model and load the Sun to illuminate it.
-- Load the asset of the Sun to illuminate the model
local sun = asset.require("scene/solarsystem/sun/transforms")
-- Load the example model from OpenSpace servers
-- If you want to use your own model, this block of code can be safely deleted
local model = asset.resource({
Name = "Animated Box",
Type = "HttpSynchronization",
Identifier = "animated_box",
Version = 1
})
local Node = {
Identifier = "RenderableModel_Example_Lighting",
Renderable = {
Type = "RenderableModel",
GeometryFile = model .. "BoxAnimated.glb",
-- Use the line below insted of the one above if you want to use your own model
--GeometryFile = "C:/path/to/model.fbx",
-- Add the Sun as a light source to illuminate the model
LightSources = {
sun.LightSource
}
},
GUI = {
Name = "RenderableModel - Lighting",
Path = "/Examples"
}
}
asset.onInitialize(function()
openspace.addSceneGraphNode(Node)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(Node)
end)
-- Model credit
--[[
Author = Cesium, https://cesium.com/
URL = https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/BoxAnimated
License =
Creative Commons Attribution 4.0 International License,
https://creativecommons.org/licenses/by/4.0/
]]
@@ -0,0 +1,43 @@
-- Vertex Colors
-- This example loads a model with vertex colors as material.
-- Load the example model from OpenSpace servers
-- If you want to use your own model, this block of code can be safely deleted
local model = asset.resource({
Name = "Vertex Colors Test Model",
Type = "HttpSynchronization",
Identifier = "model_vertex_color_test",
Version = 1
})
local Node = {
Identifier = "RenderableModel_Example_Vertex_Colors",
Renderable = {
Type = "RenderableModel",
GeometryFile = model .. "VertexColorTest.glb",
-- Use the line below insted of the one above if you want to use your own model
--GeometryFile = "C:/path/to/model.fbx",
},
GUI = {
Name = "RenderableModel - Vertex Colors",
Path = "/Examples"
}
}
asset.onInitialize(function()
openspace.addSceneGraphNode(Node)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(Node)
end)
-- Model credit
--[[
Author = Ed Mackey
URL = "https://github.com/KhronosGroup/glTF-Sample-Models/tree/main/2.0/VertexColorTest"
License =
Creative Commons Attribution 4.0 International License,
https://creativecommons.org/licenses/by/4.0/
]]
@@ -23,7 +23,7 @@ local omm = asset.resource({
local TiangongPosition = {
Identifier = "TiangongPosition",
Parent = transforms.EarthInertial.Identifier,
BoundingSphere = 54.5, -- half the width
BoundingSphere = 27.8, -- half the width
Transform = {
Translation = {
Type = "GPTranslation",
+14 -2
View File
@@ -29,6 +29,7 @@ in vec3 vs_normalViewSpace;
in vec4 vs_positionCameraSpace;
in float vs_screenSpaceDepth;
in mat3 vs_TBN;
in vec3 vs_color;
uniform float ambientIntensity = 0.2;
uniform float diffuseIntensity = 1.0;
@@ -36,6 +37,7 @@ uniform float specularIntensity = 1.0;
uniform bool performShading = true;
uniform bool use_forced_color = false;
uniform bool use_vertex_colors = false;
uniform bool has_texture_diffuse;
uniform bool has_texture_normal;
uniform bool has_texture_specular;
@@ -95,7 +97,7 @@ Fragment getFragment() {
}
// Base color
vec4 diffuseAlbedo;
vec4 diffuseAlbedo = vec4(0.0);
if (has_texture_diffuse) {
diffuseAlbedo = texture(texture_diffuse, vs_st);
}
@@ -103,6 +105,16 @@ Fragment getFragment() {
diffuseAlbedo = color_diffuse;
}
// Multiply with vertex color if specified
if (use_vertex_colors) {
diffuseAlbedo.rgb *= vs_color;
// Make sure to not go beyond color range
diffuseAlbedo.r = clamp(diffuseAlbedo.r, 0.0, 1.0);
diffuseAlbedo.g = clamp(diffuseAlbedo.g, 0.0, 1.0);
diffuseAlbedo.b = clamp(diffuseAlbedo.b, 0.0, 1.0);
}
if (performShading) {
// Specular color
vec3 specularAlbedo;
@@ -118,7 +130,7 @@ Fragment getFragment() {
}
}
// Bumb mapping
// Bump mapping
vec3 normal;
if (has_texture_normal) {
vec3 normalAlbedo = texture(texture_normal, vs_st).rgb;
+3
View File
@@ -30,12 +30,14 @@ layout(location = 0) in vec4 in_position;
layout(location = 1) in vec2 in_st;
layout(location = 2) in vec3 in_normal;
layout(location = 3) in vec3 in_tangent;
layout(location = 4) in vec3 in_color;
out vec2 vs_st;
out vec3 vs_normalViewSpace;
out float vs_screenSpaceDepth;
out vec4 vs_positionCameraSpace;
out mat3 vs_TBN;
out vec3 vs_color;
uniform mat4 modelViewTransform;
uniform mat4 projectionTransform;
@@ -51,6 +53,7 @@ void main() {
gl_Position = positionScreenSpace;
vs_st = in_st;
vs_color = in_color;
vs_screenSpaceDepth = positionScreenSpace.w;
vs_normalViewSpace =
+23 -16
View File
@@ -1,33 +1,40 @@
# Visual Test Specification
All `.ostest` files in this folders specify visual image tests that are automatically run to ensure that changes in OpenSpace do not negatively impact the rendered results. The tested results are available at https://regression.openspaceproject.com.
All `.ostest` files in these folders specify visual image tests that are automatically run to ensure that changes in OpenSpace do not negatively impact the rendered results. The test results are available at https://regression.openspaceproject.com.
The files are organized by folders, which are used as the "group" name for the test and filename of each test (without `.ostest` extension) is used as the name of the test. In general the first folder should name a profile that is being tested by the files within or a specific use-case, such as testing all renderables. Additional subfolders can be used as required within each top-level folder.
The files are organized in folders, where the folder name is used as the "group" name for the tests within, and the filename of each test (without the `.ostest` extension) is used as the name of the test. In general, the top-level folder should name a profile or a specific use case that is tested by the files within, such as testing all renderables. Additional subfolders can be used within each top-level folder.
## Test Structure
Each test must have a `screenshot` instruction as the last entry, which causes an image to be created that is used as the end result of the test. Only exactly one `screenshot` instruction per test is currently supported. Each `.ostest` file is a JSON file with two top-level keys: `profile` provides the name of the profile that should be loaded before running these test instructions, and `commands` is a list of instructions that should be executed after the profile was loaded. All instructions must have a `type` key to determine which type of instruction it is and most have a `value` key that determines the parameters for that instruction.
Each test must have a `screenshot` instruction as the **last** entry, which causes an image to be created that is used as the result of the test. Only exactly one `screenshot` instruction per test is currently supported. Each `.ostest` file is a JSON file with two top-level keys: `profile` provides the name of the profile that should be loaded before running these test instructions, and `commands` is a list of instructions that should be executed after the profile is loaded. All instructions must have a `type` and `value` key to determine which type of instruction it is and the parameters for that instruction.
By default on the servers that generate tests for https://regression.openspaceproject.com, all tests always start paused, MRF caching is enabled, and the user interface and dashboard items are disabled.
### Best practices
- By default, all tests always start paused, MRF caching is enabled, and the user interface and dashboard items are disabled
- All test should start with in instruction to set a specific time to improve reproducibility
- The few instructions there are per test, the better
- Adding `wait` instructions to ensure OpenSpace has time to load dynamic datasets increases reliability, but too many `wait`s will slow-down the over all testing
- All tests should start with the instruction to set a specific time to improve reproducibility
- The fewer instructions there are per test, the better
- Adding `wait` instructions to ensure OpenSpace has time to load dynamic datasets increases reliability, but too many `wait`s will slow down the overall testing
- Avoid `recording` and use `navigationstate` and `time` instead
- Avoid `script` if possible and use dedicated instructions when they exist. If we see the same `script` instruction used in many tests, they can be upgraded to a first-class instruction at later stage
- Avoid `script` if possible and use dedicated instructions when they exist. If we see the same `script` instruction used in several tests, they can be upgraded to a dedicated instruction at a later stage
### Instructions
- `action`: Triggers an action that must already be defined in the profile or that was defined previously in this test. The provided value must be a string that is the identifier of the action that should be triggered.
- `action`: Triggers an action that must already be defined in the profile or previously defined in the test. The provided value must be a string that is the identifier of the action that should be triggered.
Example: `{ "type": "action", "value": "os.FadeDownTrails" }`
Script Equivalent: `openspace.action.triggerAction`
- `deltatime`: Instantly changes the delta time in OpenSpace to the provided value. The provided value must be a number that is the delta time in seconds per realtime second that the engine should be set to.
- `asset`: Loads a given asset file. The provided value must be a string that is the path to the asset file to be loaded. This is specified relative to the `data/asset` folder inside OpenSpace.
Example: `{ "type": "asset", "value": "path/to/file.asset" }`
Script Equivalent: `openspace.asset.add`
- `deltatime`: Instantly changes the delta time in OpenSpace to the provided value. The provided value must be a number that is the delta time in seconds per real-time second that the engine should be set to.
Example: `{ "type": "deltatime", "value": 10 }`
Script Equivalent: `openspace.time.setDeltaTime`
- `navigationstate`: Sets the camera to the provided navigation state. The provided value must be an object that must contain at least a `anchor` and `position` and may optionally contain an `aim`, `referenceFrame`, `up`, `yaw`, `pitch`, and `timestamp`. All these values are then used to instantaneously set the position of the camera.
- `navigationstate`: Instantly moves the camera to the provided navigation state. The provided value must be an object that must contain at least an `anchor` and `position` key and may optionally contain the keys `aim`, `referenceFrame`, `up`, `yaw`, `pitch`, and `timestamp`. All these values are then used to instantaneously set the position and rotation of the camera.
Example: `{ "type": "navigationstate", "value": { "anchor": "Juno", "pitch": -0.0165756, "position": [ -22.49081, 1.191533, 26.35740 ], "up": [ 0.0288083, 0.999373, -0.0205962 ], "yaw": 0.152454 } }`
@@ -39,7 +46,7 @@ Each test must have a `screenshot` instruction as the last entry, which causes a
Script Equivalent: `openspace.time.setPause`
- `property`: Sets a specific property or group of properties to the specified value. This change is instantaneous. The provided value must contain a `property` key that is the identifier or regex for the property that should be set and a `value` key that is the new value for the property. The type of the `value` must be correct for the matched `property`.
- `property`: Instantly sets a specific property or group of properties to the specified value. The provided value must be an object containing another `property` and `value` key. The (other) `property` key is the identifier or regex for the property or properties that should be set. The (other) `value` key is the new value for the property where the type must match the (other) `property`.
Example: `{ "type": "property", "value": { "property": "Scene.Constellations.Renderable.Enabled", "value": true } }`
@@ -51,19 +58,19 @@ Each test must have a `screenshot` instruction as the last entry, which causes a
Script Equivalent: `openspace.sessionRecording.startPlayback`
- `screenshot`: Takes a screenshot of the application. At the moment, there can be only exactly one instruction of this type and it should be the last instruction in the test. This instruction also does not take any parameters
- `screenshot`: Takes a screenshot of the application. At the moment, there can be only exactly one instruction of this type and it must be the last instruction in the test. This instruction is the only one to not use the `value` key.
Example: `{ "type": "screenshot" }`
Script Equivalent: `openspace.takeScreenshot`
- `script`: Executes the script that is passed in as a value. That value must be a string that is the Lua script that is executed directly.
- `script`: Instantly executes the script that is passed in as a value. That value must be a string that is the Lua script to execute.
Example: `{ "type": "script", "value": "openspace.printError("Hello world") }`
Example: `{ "type": "script", "value": "openspace.printError('Hello world')" }`
Script Equivalent: `value`
- `time`: Sets the in-game time to the provided value. The value can be either a string, in which case it needs to be a valid date-time string, or a number, in which case it represents the number of seconds past the J2000 epoch.
- `time`: Sets the in-game time to the provided value. The value can be either a string, which needs to be a valid date-time string, or a number, which represents the number of seconds past the J2000 epoch.
Example: `{ "type": "time", "value": "2016-07-01T00:00:01.00" }`
@@ -0,0 +1,25 @@
{
"profile": "empty",
"commands": [
{ "type": "time", "value": "2024-07-11T12:00:00.00" },
{
"type": "property",
"value": { "property": "NavigationHandler.OrbitalNavigator.LimitZoom.EnabledMinimumAllowedDistance", "value": false }
},
{
"type": "asset",
"value": "examples/renderable/renderablemodel/model_vertex_colors.asset"
},
{ "type": "wait", "value": 5 },
{
"type": "navigationstate",
"value": {
"anchor": "RenderableModel_Example_Vertex_Colors",
"up": [ -0.008132849760983194, 0.9986021710677091, 0.0522260537818345 ],
"position": [0.10334103813188818, -0.20823861895763798, 3.9977746547860167 ]
}
},
{ "type": "wait", "value": 2 },
{ "type": "screenshot" }
]
}