Scenefile Documentation
Scenefiles are the way we specify the information needed to generate 3D scenes in this class. The structure documented here is specific to the way our scene parser was designed. Once lab 5 is released, you can refer to it for complementary information/specific examples.
Basic Structure
The structure of the scenefile is organized as a declaration of the globalData
, cameraData
, templateGroup
s (optional), and group
s. Scenes are specified in json format. The general format of the file is as follows:
{
"name": "root",
"globalData": {
"..."
},
"cameraData": {
"..."
},
"templateGroups": {
"..."
},
"groups": [
{
"name": "Group Name",
"translate": "...",
"rotate": "...",
"scale": "...",
"lights": [
{
"..."
}
],
"primitives": [
{
"..."
}
],
"groups": [
{
"..."
}
]
}
]
}
Below we have a few of the most basic field-value pairings we use for attributes of the scenefile. This is mainly included so as to be referenced in later sections and so that you can get a feel for the syntax of the scenefile.
float
— a single floating point number with syntax (ex:10.2
):{ "field": 10.2 }
vec3
— three floating point numbers that represent a vector for a displacement or scaling factors in the x-y-z plane with syntax (ex:(10.2, 3.9, 8.6)
):{ "field": [10.2, 3.9, 8.6] }
vec4
- four floating point numbers that represent the axis of rotation (x, y, z) as well as the angle of rotation in degrees (w) with syntax (ex:(1.0, 0.0, 0.0), 90°
):{ "field": [1.0, 0.0, 0.0, 90] }
RGBSchema
— three floating point numbers that represent a color in the red-green-blue (values 0-1) color space with syntax (ex:R: 0.5, G: 0.5, B: 0.5
):{ "field": [0.5, 0.5, 0.5] }
string
- a string of characters that represents a file path, name, primitive/light type, with syntax (ex:"hello world"
):{ "field": "hello world" }
non-negative max-1 float
- a single floating point number with minimumvalue
0.0 and maximum value1.0
that represents coefficients for lighting properties (where returning >1 or <0 of incoming light would be illogical) with syntax (ex:0.5
):{ "field": 0.5 }
Mat4Schema
- a 4-length array containing 4-length arrays offloat
that represent a transformation with syntax:{ "field": [ [1.3, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1] ] }
While these represent the basic value types, there are more complex types that correspond to how exactly a light
, primitive
, and so forth are represented in the scene. These are explained in more depth below.
Global Data
Global data specifies certain constants used while rendering. Each of the coefficients has a certain place in the lighting equation; they are all non-negative max-1 float
that are used to scale the diffuse, ambient, transparent, and specular components respectively. These constants are helpful for scenes with very few or very many lights. An example of globalData
is as follows:
"globalData": {
"ambientCoeff": 0.5,
"diffuseCoeff": 0.7,
"specularCoeff": 0.54,
"transparentCoeff": 1
}
Camera Data
There can only be one camera in the scene. In a camera block, there must be listed the position (position
), look vector (look
), up vector (up
), and height angle (heightAngle
). Note that it is possible to replace the look vector with a focal point (denoted by the keyword focus
), if so desired. The camera data must not contain both look
and focus
properties. Also note that the two fields for aperature and focal length are optional and only need to be included when implementing certain extra credit features. An example of a cameraData
is as follows:
"cameraData": {
"position": [0, 0, 5],
"up": [0, 1, 0],
"look": [0, 0, 1],
"heightAngle": 45,
"aperture": 3, // optional
"focalLength": 5 // optional
}
Groups
groups
are the most substantial building block of scenefiles. Essentially, group
s are wrappers that allow us to apply transformations to contained primitive
s, light
s, or even other group
s. Nested group
s allow us to position objects relative to one another when helpful (as a contained group
will still possess the transformations declared by the parent group
).
A group
optionally possesses a name (which become useful for templateGroups
as explained later), as well as a transformation defined by fields of rotate
, translate
, scale
which are of form vec4
, vec3
, and vec3
respectively. Alternatively, a transformation may be defined as a Mat4Schema
matrix
. Additionally, a group
contains an array of primitive
and/or light
. Typically, a group
will contain only a single light
or a primitive
as all primitives and objects within the same exact group
possess the identical transformations and as a result would be stacked on top of one another. Finally, a group
may also contain its own subgroup
s to take advantage of the recursive transformation process as discussed before in the groups
field. An example group could be as follows (with the primitives
array and groups
array contents excluded):
{
"name": "Group Name",
"translate": [0, 0, 0],
"rotate": [0, 0, 0],
"scale": [1, 1, 1],
"primitives": [
{
"..."
}
],
"groups": [
{
"..."
}
]
}
Template Groups
templateGroups
are identical to groups except that they require a name and are not rendered. Instead, templateGroups
may be declared and then reused within the first real groups
array as a manner by which to reduce the amount of repeated code. An example of a templateGroup
is as follows:
{
"name": "root",
"..."
"templateGroups": {
{
"name": "Template Group 1",
"translate": [0, 0, 0],
"rotate": [0, 0, 0],
"scale": [1, 1, 1],
"primitives": [
{
"..."
}
]
}
},
"groups": [
{
"name": "Group Name",
"translate": "...",
"rotate": "...",
"scale": "...",
"groups": [
{
"name": "Template Group 1"
}
]
}
]
}
As you can see, templateGroups
are instantiated by the reusing the name of the templateGroup
within the groups
array of the root
object, as well as any subsequent groups
array. Note that templateGroups
may also contain other templateGroups
within their groups
array.
Lights
The specific behavior of a light
is defined by the type
field contained within itself that is of type string
. The type
field may be one of the following: point
, directional
, or spot
. Regardless of type
, a light may possess a name
and must possess color
of RGBSchema
type. Regarding specific light types...
point
lights must contain aattenuationCoeff
ofvector
type.directional
lights must contain adirection
ofvector
type.spot
lights must contain adirection
ofvector
type as well as apenumbra
andangle
offloat
type.
An example of a light
is as follows:
{
"name": "Point Light 1",
"type": "point",
"color": [0.5, 0.5, 0.5],
"attenuationCoeff": [0.5, 0.5, 0.5]
}
Note that with regards to directional lights, ALL translations applied are ignored (as a directional light is simulated as a point inifinitely far away in the given direction). Similarly, spot and point lights are affected by translations, but not rotation or scaling operations.
Primitives
A Primitive
is the most basic object and are at the lowest level what the scene is comprised of. Primitives are those which represent three-dimensional shapes, namely, cube
, cylinder
, cone
, sphere
, and mesh
which are specified by the type
field of type string
.
Primitives contain in their block a combination of optional surface characteristic definitions: diffuse color diffuse
, ambient color ambient
, reflected color reflective
, specular color specular
, specular exponent shininess
, transparency transparent
. There is also the ability to provide texture
to an object using a bitmap or to use a bumpmap
. Both of these fields have optional u,v coefficient fields for the scaling of the texture which both default to a value of 1 if not specified. Primitives may also have an optional name. All fields of primitive
may be represented as follows:
{
"name": "Primitive Name",
"type": "cube",
"diffuse": [0.5, 0.5, 0.5],
"specular": [0.5, 0.5, 0.5],
"reflective": [0.5, 0.5, 0.5],
"transparent": [0.5, 0.5, 0.5],
"shininess": 10,
"texture": "path/to/texture.png", // optional (with U,V)
"textureU": 0.5,
"textureV": 0.5,
"bumpMap": "path/to/bumpmap.png", // optional (with U,V)
"bumpMapU": 0.5,
"bumpMapV": 0.5,
"meshFile": "path/to/mesh.obj" // if type: mesh
}
Note that the above primitive would never be seen in the wild. For example, a meshFile
may only be provided if the primitive
is of type mesh
. Similarly, textureU
and textureV
ought only be provided if texture
is provided. The same goes for bumpMapU
and bumpMapV
. The following is a more realistic example of a primitive
:
{
"name": "Primitive Name",
"type": "cube",
"diffuse": [0.5, 0.5, 0.5],
"specular": [0.5, 0.5, 0.5],
"reflective": [0.5, 0.5, 0.5],
"shininess": 10,
"texture": "path/to/texture.png",
"textureU": 0.5,
"textureV": 0.5
}
Comprehensive Sample List
Below we have a simple scenefile composed of just a single directional
light and single cube
.
{
"name": "root",
"globalData": {
"ambientCoeff": 0.5,
"diffuseCoeff": 0.5,
"specularCoeff": 0.5,
"transparentCoeff": 0.5
},
"cameraData": {
"position": [3, 3, 3],
"up": [0, 1, 0],
"heightAngle": 30,
"focus": [0, 0, 0]
},
"groups": [
{
"name": "root",
"lights": [
{
"type": "directional",
"color": [1, 1, 1],
"direction": [-3, -2, -1]
}
],
"primitives": [
{
"type": "cube",
"diffuse": [1, 0, 0],
"specular": [1, 1, 1],
"shininess": 25
}
]
}
]
}
Below is a more complex scene featuring 2 point
lights, 1 directional
light, 2 cones
, 1 cube
, 1 cylinder
, and 1 sphere
with a nested group
that applies inherited transformations.
{
"name": "root",
"globalData": {
"ambientCoeff": 0.2,
"diffuseCoeff": 0.5,
"specularCoeff": 0.5,
"transparentCoeff": 1
},
"cameraData": {
"position": [0.0, 0.0, 5.0],
"up": [0.0, 1.0, 0.0],
"look": [0, 0, 0],
"heightAngle": 45.0
},
"groups": [
{
"lights": [
{
"type": "directional",
"color": [1.0, 1.0, 1.0],
"direction": [-2.0, -4.0, -6.0]
}
]
},
{
"translate": [9.0, -3.0, 1.0],
"lights": [
{
"type": "point",
"color": [0.5, 0.5, 0.5],
"attenuationCoeff": [1, 0, 0]
}
]
},
{
"translate": [-9.0, -3.0, 1.0],
"lights": [
{
"type": "point",
"color": [0.5, 0.5, 0.5],
"attenuationCoeff": [1, 0, 0]
}
]
},
{
"groups": [
{
"translate": [0.0, 0.0, -5.0],
"scale": [1.5, 1.5, 1.5],
"primitives": [
{
"type": "cylinder",
"ambient": [0.0, 0.5, 0.0],
"diffuse": [0.0, 1.0, 0.0],
"specular": [1.0, 1.0, 1.0],
"shininess": 30.0
}
]
},
{
"translate": [0.0, 0.0, -8.0],
"rotate": [0.0, 0.0, 1.0, 90.0],
"groups": [
{
"translate": [4.0, 0.0, 0.0],
"scale": [3.0, 3.0, 3.0],
"primitives": [
{
"type": "sphere",
"ambient": [0.0, 0.5, 0.5],
"diffuse": [0.0, 1.0, 1.0],
"specular": [1.0, 1.0, 1.0],
"shininess": 30.0
}
]
},
{
"translate": [-4.0, 0.0, 0.0],
"scale": [3.0, 3.0, 3.0],
"primitives": [
{
"type": "cone",
"ambient": [0.5, 0.5, 0.0],
"diffuse": [1.0, 1.0, 0.0],
"specular": [1.0, 1.0, 1.0],
"shininess": 30.0
}
]
},
{
"translate": [0.0, 4.0, 0.0],
"scale": [3.0, 3.0, 3.0],
"primitives": [
{
"type": "cube",
"ambient": [0.5, 0.0, 0.5],
"diffuse": [1.0, 0.0, 1.0],
"specular": [1.0, 1.0, 1.0],
"shininess": 30.0
}
]
},
{
"translate": [0.0, -4.0, 0.0],
"scale": [3.0, 3.0, 3.0],
"primitives": [
{
"type": "cone",
"ambient": [0.5, 0.5, 0.5],
"diffuse": [1.0, 1.0, 1.0],
"specular": [1.0, 1.0, 1.0],
"shininess": 30.0
}
]
}
]
}
]
}
]
}
Finally, below we have a scenefile that features 1 point
lights and 1 templateGroup
containing a sphere
with numerous material properties as well as a normal group that creates another sphere
as well as instantiates that templateGroup
.
{
"name": "root",
"globalData": {
"ambientCoeff": 0.5,
"diffuseCoeff": 0.7,
"specularCoeff": 0.54,
"transparentCoeff": 1
},
"cameraData": {
"position": [10, 4.1, 16],
"up": [0, 1, 0],
"heightAngle": 49.5,
"look": [-9, -3.2, -16]
},
"templateGroups": [
{
"name": "level 1",
"translate": [0, 0, 0],
"scale": [3, 3, 3],
"primitives": [
{
"type": "sphere",
"reflective": [0.75, 1, 0.75],
"diffuse": [0.75, 1, 0.75],
"shininess": 25,
"specular": [1, 1, 1],
"textureFile": "image/marsTexture.png",
"textureU": 1,
"textureV": 1,
"blend": 0.75
}
]
}
],
"groups": [
{
"name": "Point Light",
"translate": [10, 10, 10],
"lights": [
{
"type": "point",
"color": [1, 1, 1],
"attenuationCoeff": [0, 0, 0]
}
]
},
{
"name": "level 0",
"translate": [0, 0, 0],
"scale": [6, 6, 6],
"primitives": [
{
"type": "sphere",
"reflective": [1, 0.75, 0.75],
"diffuse": [1, 0.75, 0.75],
"shininess": 25,
"specular": [1, 1, 1],
"textureFile": "image/marsTexture.png",
"textureU": 1,
"textureV": 1,
"blend": 0.75
}
]
},
{
"translate": [-4.5, 0, 0],
"rotate": [0, 0, 1, 90],
"groups": [
{
"name": "level 1"
}
]
}
]
}
This format is not as all-encompassing as it could be, but this is intentional as one of the major aims is for it to be simple to parse.