Skip to main content

Post-processing tools 2025 R1

Test008

Last update: 16.07.2025

/// <script src="./ansys251/nexus/viewer-loader.js"></script> /// ///

Animated textured sphere.

See Test006.cpp for more information about animations.

This is another animation example that simply transforms a node matrix over a period of time. The underlying code in the ANSYSViewer extracts a quaternion from the matrix and interpolates between quaternions at the defined times.

The animation in this example merely rotates the sphere about its Z axis.

A custom shader is used that demonstrates the usage of a 2D texture coordinate. The 2D texture coordinates in this example are similar to latitude and longitude.

/*
* Copyright 2018-2021 ANSYS, Inc. Unauthorized use, distribution, or duplication is prohibited.
*
* Restricted Rights Legend
*
* Use, duplication, or disclosure of this
* software and its documentation by the
* Government is subject to restrictions as
* set forth in subdivision [(b)(3)(ii)] of
* the Rights in Technical Data and Computer
* Software clause at 52.227-7013.
*/
#include <vector>
#include "GLTFWriter.h"
#include "test.h"
using namespace ANSYS::AVZ;
// Lighted Textured Sphere
TESTFUNC(LightedTexturedSphere)
{
GLTFWriter::GLTF *gltf = GLTFWriter::GLTF::Create("MyApp", "1.0", functionName.c_str(), type);
if (!gltf)
throw std::runtime_error("Can't create GLTF");
// SCENE
GLTFWriter::Scene *scene = GLTFWriter::Scene::Create(gltf, "TestScene", "m", 1.0, GLTFWriter::Scene::BT_SOLID, 0.5, 0.5, 0.5);
if (!scene) {
GLTFWriter::GLTF::Destroy(gltf);
throw std::runtime_error("Can't create scene");
}
// ANIMATION
GLTFWriter::Animation *animation = GLTFWriter::Animation::Create(gltf, "My Animation");
// create a reusable time
{
float _time[4];
unsigned int i = 0;
_time[i++] = 0; // time 0
_time[i++] = 10; // time 1
_time[i++] = 20; // time 2
_time[i++] = 30; // time 3
time = GLTFWriter::Attribute::Create(gltf, "TIME", GLTFWriter::Attribute::AT_FLOAT, 4, _time);
}
// ANIMATION SAMPLER for sampling matrix
{
float _mat[4 * 16];
unsigned int i = 0;
// time 0
GLTFWriter::Test::Matrix4 m0; // identity
for (unsigned int j = 0; j < 16; ++j)
_mat[i++] = (float)m0[j];
// time 1
GLTFWriter::Test::Matrix4 m1; // identity
GLTFWriter::Test::Matrix4 r1;
r1.LoadRotation(GLTFWriter::Test::Vector3(0, 0, 0), GLTFWriter::Test::Vector3(0, 1, 0), DegreesToRadians(120));
m1 *= r1;
for (unsigned int j = 0; j < 16; ++j)
_mat[i++] = (float)m1[j];
// time 2
GLTFWriter::Test::Matrix4 m2; // identity
GLTFWriter::Test::Matrix4 r2;
r2.LoadRotation(GLTFWriter::Test::Vector3(0, 0, 0), GLTFWriter::Test::Vector3(0, 1, 0), DegreesToRadians(240));
m2 *= r2;
for (unsigned int j = 0; j < 16; ++j)
_mat[i++] = (float)m2[j];
// time 3
GLTFWriter::Test::Matrix4 m3; // identity
GLTFWriter::Test::Matrix4 r3;
r3.LoadRotation(GLTFWriter::Test::Vector3(0, 0, 0), GLTFWriter::Test::Vector3(0, 1, 0), DegreesToRadians(360));
m3 *= r3;
for (unsigned int j = 0; j < 16; ++j)
_mat[i++] = (float)m3[j];
GLTFWriter::Attribute *matrix = GLTFWriter::Attribute::Create(gltf, "MATRIX", GLTFWriter::Attribute::AT_FLOAT_MAT4, 4, _mat);
sampler = GLTFWriter::AnimationSampler::Create(gltf, time, matrix);
}
// LIGHTS
{
// LIGHT NODE
GLTFWriter::Node *lightNode = GLTFWriter::Node::CreateLight(gltf);
if (!lightNode || !scene->SetLight(lightNode)) {
GLTFWriter::GLTF::Destroy(gltf);
throw std::runtime_error("Can't create light");
}
// LIGHTS
GLTFWriter::Light *light1 = GLTFWriter::Light::CreateAmbient(gltf);
lightNode->AppendLight(light1);
GLTFWriter::Light *light2 = GLTFWriter::Light::CreateDirectional(gltf, 1, 1, 1, -1, -1, -1);
lightNode->AppendLight(light2);
}
// CAMERA
{
// CAMERA
GLTFWriter::Camera *camera = GLTFWriter::Camera::CreatePerspective(gltf);
// CAMERA NODE
std::vector<double> mat(16);
mat[0] = 0.5;
mat[1] = 0;
mat[2] = 0;
mat[3] = 0;
mat[4] = 0;
mat[5] = 0.5;
mat[6] = 0;
mat[7] = 0;
mat[8] = 0;
mat[9] = 0;
mat[10] = 0.5;
mat[11] = 0;
mat[12] = 0;
mat[13] = 0;
mat[14] = 0;
mat[15] = 1;
GLTFWriter::Node *cameraNode = GLTFWriter::Node::CreateCamera(gltf, camera, "TestCamera", &mat[0]);
scene->SetCamera(cameraNode);
}
// generate vertices, normals, and texture coordinates for a ball
std::vector<float> vertices;
std::vector<float> normals;
std::vector<float> texCoords;
std::vector<unsigned short> triangles;
GLTFWriter::Test::MakeTexturedSphere(0, 0, 0, 0.5, 64, vertices, normals, texCoords, triangles);
// MESH NODE
{
// NODE
std::vector<double> mat(16);
mat[0] = 1;
mat[1] = 0;
mat[2] = 0;
mat[3] = 0;
mat[4] = 0;
mat[5] = 1;
mat[6] = 0;
mat[7] = 0;
mat[8] = 0;
mat[9] = 0;
mat[10] = 1;
mat[11] = 0;
mat[12] = 0;
mat[13] = 0;
mat[14] = 0;
mat[15] = 1;
GLTFWriter::Node *node = GLTFWriter::Node::CreateMesh(gltf, "Sphere", true, &mat[0]);
if (!node || !scene->AppendMesh(node)) {
GLTFWriter::GLTF::Destroy(gltf);
throw std::runtime_error("Can't create mesh node");
}
// MESH
GLTFWriter::Mesh *mesh = GLTFWriter::Mesh::Create(gltf);
if (!mesh || !node->AppendMesh(mesh)) {
GLTFWriter::GLTF::Destroy(gltf);
throw std::runtime_error("Can't create mesh");
}
// TEXTURE
GLTFWriter::Texture *texture = GLTFWriter::Test::CreateTexture(gltf, "earth.png");
// VERTEX SHADER
std::string vsText(std::string("precision highp float;\n") +
"uniform mat4 u_projectionMatrix;\n" +
"uniform mat4 u_modelViewMatrix;\n" +
"attribute vec3 a_position;\n" +
"uniform mat3 u_normalMatrix;\n" +
"attribute vec3 a_normal;\n" +
"varying vec3 v_eyePosition;\n" +
"varying vec3 v_normal;\n" +
"attribute vec2 a_texCoord0;\n" +
"varying vec2 v_texCoord0;\n" +
"void main() {\n" +
"vec4 l_position = vec4(a_position, 1.0);\n" +
"vec4 l_eyePosition = u_modelViewMatrix * l_position;\n" +
"v_eyePosition = l_eyePosition.xyz / l_eyePosition.w;\n" +
"v_normal = u_normalMatrix * a_normal;\n" +
"v_texCoord0 = a_texCoord0;\n" +
"gl_Position = u_projectionMatrix * l_eyePosition;\n" +
"}\n");
std::vector<const char *> uVertex;
uVertex.push_back("u_modelViewMatrix");
uVertex.push_back("u_projectionMatrix");
uVertex.push_back("u_normalMatrix");
std::vector<const char *> aVertex;
aVertex.push_back("a_position");
aVertex.push_back("a_normal");
aVertex.push_back("a_texCoord0");
GLTFWriter::Shader *vertexShader = GLTFWriter::Shader::Create(gltf, vsText.c_str(), (unsigned int)aVertex.size(), &aVertex[0], (unsigned int)uVertex.size(), &uVertex[0]);
// FRAGMENT SHADER
std::string fsText(std::string("precision highp float;\n") +
"uniform vec3 u_light0Position;\n" +
"uniform vec3 u_light1Ambient;\n" +
"uniform sampler2D u_texture0;\n" +
"varying vec3 v_eyePosition;\n" +
"varying vec3 v_normal;\n" +
"varying vec2 v_texCoord0;\n" +
"void main() {\n" +
"vec4 l_color = vec4(1.0, 1.0, 1.0, 1.0);\n" +
"vec3 l_normal = normalize(v_normal);\n" +
"vec4 l_texelColor = texture2D(u_texture0, v_texCoord0);\n" +
"l_color *= l_texelColor;\n" +
"vec3 l_frontAmbient = u_light1Ambient * l_color.rgb;\n" +
"vec3 l_frontDiffuse = vec3(0.0, 0.0, 0.0);\n" +
"vec3 l_frontSpecular = vec3(0.0, 0.0, 0.0);\n" +
"vec3 l_VP = normalize(u_light0Position - v_eyePosition);\n" +
"float l_nDotVP = dot(l_normal, l_VP);\n" +
"if (!gl_FrontFacing)\n" + // two sided lighting
"l_nDotVP = -l_nDotVP;\n" +
"if (l_nDotVP > 0.0) {\n" + // front faced
"l_frontDiffuse += l_nDotVP * l_color.rgb;\n" +
"vec3 l_halfVector = normalize(l_VP + vec3(0.0, 0.0, 1.0));\n" + // eye is always looking down Z axis
"float l_nDotHV = dot(l_normal, l_halfVector);\n" +
"if (!gl_FrontFacing)\n" + // two sided lighting
"l_nDotHV = -l_nDotHV;\n"
"if (l_nDotHV > 0.0)\n" + // specular
"l_frontSpecular += pow(l_nDotHV, 100.0) * vec3(1.0, 1.0, 1.0);\n" +
"}\n" + // front faced
"l_color = vec4(l_frontAmbient + l_frontDiffuse + l_frontSpecular, l_color.a);\n" +
"l_color = clamp(l_color, 0.0, 1.0);\n" +
"gl_FragColor = l_color;\n" +
"}\n");
std::vector<const char *> uFragment;
uFragment.push_back("u_light0Position");
uFragment.push_back("u_light1Ambient");
uFragment.push_back("u_texture0");
GLTFWriter::Shader *fragmentShader = GLTFWriter::Shader::Create(gltf, fsText.c_str(), 0, 0, (unsigned int)uFragment.size(), &uFragment[0]);
// PROGRAM
GLTFWriter::Program *program = GLTFWriter::Program::Create(gltf, vertexShader, fragmentShader);
// TECHNIQUE
GLTFWriter::Technique *technique = GLTFWriter::Technique::Create(gltf, program);
if (!technique ||
// TECHNIQUE PARAMETERS
!technique->AppendParameter(GLTFWriter::Parameter::Create(gltf, "u_light0Position", "light0Position", GLTFWriter::Parameter::PT_FLOAT_VEC3, GLTFWriter::Value::Create(gltf, "", 0.0, 0.0, 1.0))) ||
!technique->AppendParameter(GLTFWriter::Parameter::Create(gltf, "u_light1Ambient", "light1Ambient", GLTFWriter::Parameter::PT_FLOAT_VEC3, GLTFWriter::Value::Create(gltf, "", 0.0, 0.0, 0.0))) ||
!technique->AppendParameter(GLTFWriter::Parameter::CreateModelViewMatrix(gltf, "u_modelViewMatrix")) ||
!technique->AppendParameter(GLTFWriter::Parameter::CreateProjectionMatrix(gltf, "u_projectionMatrix")) ||
!technique->AppendParameter(GLTFWriter::Parameter::CreateNormalMatrix(gltf, "u_normalMatrix")) ||
!technique->AppendParameter(GLTFWriter::Parameter::Create(gltf, "u_texture0", "texture0", GLTFWriter::Parameter::PT_SAMPLER_2D)) ||
!technique->AppendParameter(GLTFWriter::Parameter::Create(gltf, "a_position", "POSITION", GLTFWriter::Parameter::PT_FLOAT_VEC3)) ||
!technique->AppendParameter(GLTFWriter::Parameter::Create(gltf, "a_normal", "NORMAL", GLTFWriter::Parameter::PT_FLOAT_VEC3)) ||
!technique->AppendParameter(GLTFWriter::Parameter::Create(gltf, "a_texCoord0", "TEXCOORD0", GLTFWriter::Parameter::PT_FLOAT_VEC2)) ||
// TECHNIQUE STATES
!technique->AppendState(GLTFWriter::State::Create(gltf, GLTFWriter::State::ST_BLENDENABLE, 1)) ||
!technique->AppendState(GLTFWriter::State::Create(gltf, GLTFWriter::State::ST_DEPTHTESTENABLE, 1))) {
GLTFWriter::GLTF::Destroy(gltf);
throw std::runtime_error("Can't create technique");
}
// MATERIAL
GLTFWriter::Material *material = GLTFWriter::Material::Create(gltf, technique);
if (!material ||
// MATERIAL UNIFORM VALUES
!material->AppendValue(GLTFWriter::Value::Create(gltf, "light0Position", 100.0, 100.0, 100.0)) ||
!material->AppendValue(GLTFWriter::Value::Create(gltf, "light1Ambient", 0.3, 0.3, 0.3)) ||
!material->AppendValue(GLTFWriter::Value::Create(gltf, "texture0", texture->GetID())) ||
// this is a solid object, that we want to have an interior color so when it is clipped we see the solid interior color
!material->AppendValue(GLTFWriter::Value::Create(gltf, "solidColor", 0.6, 0.4, 0.2, 1.0))) {
GLTFWriter::GLTF::Destroy(gltf);
throw std::runtime_error("Can't create material");
}
// POSITION ATTRIBUTE
const unsigned int numVertices = (unsigned int)vertices.size() / 3;
GLTFWriter::Attribute *vertex = GLTFWriter::Attribute::Create(gltf, "POSITION", GLTFWriter::Attribute::AT_FLOAT_VEC3, numVertices, &vertices[0]);
// INDICES
GLTFWriter::Index *index = GLTFWriter::Index::Create(gltf, (unsigned int)triangles.size(), &triangles[0]);
// TEXCOORD ATTRIBUTE
GLTFWriter::Attribute *texCoord = GLTFWriter::Attribute::Create(gltf, "TEXCOORD0", GLTFWriter::Attribute::AT_FLOAT_VEC2, numVertices, &texCoords[0]);
// NORMAL ATTRIBUTE
GLTFWriter::Attribute *normal = GLTFWriter::Attribute::Create(gltf, "NORMAL", GLTFWriter::Attribute::AT_FLOAT_VEC3, numVertices, &normals[0]);
// PRIMITIVE
GLTFWriter::Primitive *primitive = GLTFWriter::Primitive::Create(gltf, GLTFWriter::Primitive::PT_TRIANGLES, material, index);
if (!primitive ||
!primitive->AppendAttribute(vertex) ||
!primitive->AppendAttribute(normal) ||
!primitive->AppendAttribute(texCoord) ||
!mesh->AppendPrimitive(primitive)) {
GLTFWriter::GLTF::Destroy(gltf);
throw std::runtime_error("Can't create primitive");
}
animation->AppendChannel(sampler, node, "matrix");
}
if (!gltf->Write()) {
GLTFWriter::GLTF::Destroy(gltf);
throw std::runtime_error("Error creating file");
}
GLTFWriter::GLTF::Destroy(gltf);
if (error != GLTFWriter::GLTF::GLTF_ERROR_NONE)
throw std::runtime_error("Error creating file");
}
Animation samplers define mechanisms for defining how animation channels change over time.
Animations define mechanisms for changing over time, node properties and material values.
virtual bool AppendChannel(AnimationSampler *sampler, Node *target, const char *path)=0
Attributes define the per element index values for elements defined by Index.
Cameras define an orthographic or perspective projection of the scene.
Definition GLTFCamera.h:28
This is the main class of the GLTFWriter.
Definition GLTFGLTF.h:32
virtual bool Write(bool formatJSON=false)=0
virtual GLTFError GetError()=0
Indexes define elements within primitives.
Definition GLTFIndex.h:29
Lights define the light objects that can be added to a light node.
Definition GLTFLight.h:31
Materials describe how primitives are rendered.
virtual bool AppendValue(Value *value)=0
Meshes define the renderable objects that can be added to a node.
Definition GLTFMesh.h:104
virtual bool AppendPrimitive(Primitive *primitive)=0
Nodes are the GLTFWriter class that contain the data that is defined in the GLTF file.
Definition GLTFNode.h:31
virtual bool AppendLight(Light *light)=0
virtual bool AppendMesh(Mesh *mesh)=0
Primitives are the renderable parts of meshes.
Definition GLTFMesh.h:30
virtual bool AppendAttribute(Attribute *attribute)=0
Programs are the GLSL executable code for rendering the primitives.
Definition GLTFProgram.h:28
Scenes are the GLTFWriter class that create the view of the data that is defined in the GLTF file.
Definition GLTFScene.h:29
virtual bool AppendMesh(Node *mesh)=0
virtual bool SetLight(Node *light)=0
virtual bool SetCamera(Node *camera)=0
Shaders are the GLSL executable code for vertex and fragment.
Definition GLTFShader.h:26
Techniques performs the rendering of primitives.
virtual bool AppendState(State *state)=0
virtual bool AppendParameter(Parameter *parameter)=0
Textures are images that can be used to color a primitive.
Definition GLTFTexture.h:28

Connect with Ansys