Difference between revisions of "Horde3D Wiki:HOWTO"

From Horde3D Wiki
Jump to: navigation, search
m
 
(10 intermediate revisions by 6 users not shown)
Line 1: Line 1:
 +
__NOEDITSECTION__{{ContentBlock|color=white|content={{!!}}
 
The HOWTO contains quick answers for smaller problems.  
 
The HOWTO contains quick answers for smaller problems.  
  
Line 22: Line 23:
 
</source>}}
 
</source>}}
 
If you want to get the dimensions of mipmap level ''n'', change the elemIdx parameter which is 0 above to ''n''.
 
If you want to get the dimensions of mipmap level ''n'', change the elemIdx parameter which is 0 above to ''n''.
 +
 +
 +
== How to procedurally generate a texture ==
 +
 +
To generate a texture and bind it to a shader, you need to perform three steps:
 +
 +
First, create the texture handle, map data stream and write to it. Note that you need to perform this step '''before''' loading resources from disk (using <code>H3dutLoadResourcesFromDisk()</code>)! Otherwise, the texture creation will fail.
 +
{{CppSourceCode|
 +
description= C++ Code|
 +
code=
 +
<source lang="cpp" line="1">
 +
H3DRes texRes = h3dCreateTexture( "myTexRes", 64, 64, H3DFormats::TEX_RGBA16F, H3DResFlags::NoTexMipmaps );
 +
float *data = static_cast<float*>( h3dMapResStream( texRes, H3DTexRes::ImageElem, 0, H3DTexRes::ImgPixelStream, false, true ) );
 +
for( int row=0; row<64; ++row )
 +
{
 +
    float *rowPtr = data + row*64*4;
 +
    for( int col=0; col<64; ++col )
 +
    {
 +
        float *sample = rowPtr + col*4;
 +
        sample[0] = ...; // R
 +
        sample[1] = ...; // G
 +
        sample[2] = ...; // B
 +
        sample[3] = ...; // A
 +
    }
 +
}
 +
h3dUnmapResStream( texRes );
 +
</source>}}
 +
 +
Second, to bind the texture to a shader, you need to include it in the material XML file like this:
 +
 +
<source lang="xml">
 +
<Material>
 +
    <Shader source="shaders/myshader.shader" />
 +
    <Sampler name="myTexture" map="myTexRes" />
 +
</Material>
 +
</source>
 +
 +
Finally, to reference the sampler in the shader, you need to declare it like this:
 +
{{CppSourceCode|
 +
description= Shader Code|
 +
code=
 +
<source lang="cpp" line="1">
 +
[[FX]]
 +
sampler myTexture;
 +
 +
// ... declare other samplers, contexts etc.
 +
 +
[[FS_LIGHTING]]
 +
 +
varying vec2 texCoords; // ... assumed given by vertex shader ...
 +
uniform sampler2D myTexture;
 +
 +
void main( void )
 +
{
 +
  gl_FragColor = texture2D( myTexture, texCoords );
 +
}
 +
</source>}}
 +
Information taken from [http://www.horde3d.org/forums/viewtopic.php?f=2&t=965 this thread] on the forums.
 +
 +
 +
== How to procedurally generate a geometry resource  ==
 +
To generate a geometry resource from given data call '''h3dutCreateGeometryRes''':
 +
 +
the following example will create a simple 2D plane.
 +
 +
{{CppSourceCode|
 +
description= C++ Code|
 +
code=
 +
<source lang="cpp" line="1">
 +
float posData[] = {
 +
  0,  0, 0,
 +
10,  0, 0,
 +
  0, 10, 0,
 +
10, 10, 0
 +
};
 +
 +
unsigned int indexData[] = { 0, 1, 2, 2, 1, 3 };
 +
short normalData[] = {
 +
0, 0, 1,
 +
0, 0, 1,
 +
0, 0, 1,
 +
0, 0, 1
 +
};
 +
 +
float uvData[] = {
 +
0, 0,
 +
1, 0,
 +
0, 1,
 +
1, 1
 +
};
 +
 +
H3DRes geoRes = h3dutCreateGeometryRes( "geoRes", 4, 6, posData, indexData, normalData, 0, 0, uvData, 0 );
 +
H3DNode model = h3dAddModelNode( H3DRootNode, "DynGeoModelNode", geoRes );
 +
h3dAddMeshNode( model, "DynGeoMesh", matRes, 0, 6, 0, 3 );
 +
</source>}}
  
  
Line 54: Line 150:
 
H3DRes matRes = h3dGetNodeParamI( myMesh, H3DMesh::MatResI );
 
H3DRes matRes = h3dGetNodeParamI( myMesh, H3DMesh::MatResI );
 
int idx = h3dFindResElem( matRes, H3DMatRes::SamplerElem, H3DMatRes::SampNameStr, "albedoMap" );
 
int idx = h3dFindResElem( matRes, H3DMatRes::SamplerElem, H3DMatRes::SampNameStr, "albedoMap" );
h3dSetResParamI( matRes, H3DMatRes::SamplerElem, elemIdx, SampTexResI, myNewTex );
+
h3dSetResParamI( matRes, H3DMatRes::SamplerElem, idx, H3DMatRes::SampTexResI, myNewTex );
 
</source>}}
 
</source>}}
  
Line 60: Line 156:
  
 
'''Note:''' The code above changes directly the material resource. If several meshes are using the same material, all of them will have the new texture. If that is not desired, you can create a private copy of the material by cloning it.
 
'''Note:''' The code above changes directly the material resource. If several meshes are using the same material, all of them will have the new texture. If that is not desired, you can create a private copy of the material by cloning it.
 +
 +
 +
== How to display text in the scene ==
 +
 +
Usually, text is displayed as part of a GUI or HUD on top of the scene. However, sometimes it can be useful to display text inside the scene at the location of a specific node. The following snippet displays the names of all models at the nodes' origins. For obtaining the right screen position, the node positions are projected and converted to overlay coordinates.
 +
 +
{{CppSourceCode|description= C++ Code|code=
 +
<source lang="cpp" line="1">
 +
// Get camera view matrix
 +
const float *camTrans;
 +
h3dGetNodeTransMats( _cam, 0x0, &camTrans );
 +
Matrix4f viewMat( Matrix4f( camTrans ).inverted() );
 +
 +
// Get camera projection matrix
 +
float camProj[16];
 +
h3dGetCameraProjMat( _cam, camProj );
 +
Matrix4f projMat( camProj );
 +
 +
// Loop over all model nodes
 +
int cnt = h3dFindNodes( H3DRootNode, "", H3DNodeTypes::Model );
 +
for( int i = 0; i < cnt; ++i )
 +
{
 +
// Get node name
 +
H3DNode node = h3dGetNodeFindResult( i );
 +
const char *name = h3dGetNodeParamStr( node, H3DNodeParams::NameStr );
 +
 +
// Get node position
 +
const float *nodeTrans;
 +
h3dGetNodeTransMats( node, 0, &nodeTrans );
 +
Vec4f pos( nodeTrans[12], nodeTrans[13], nodeTrans[14], 1 );
 +
 +
// Project
 +
pos = projMat * viewMat * pos;
 +
float x = pos.x / pos.w;
 +
float y = pos.y / pos.w;
 +
 +
// Transform to overlay coordinates
 +
    const float aspectRatio = h3dGetViewportParams( 0x0, 0x0, 0x0, 0x0 );
 +
x = (x * 0.5f + 0.5f)*aspectRatio;
 +
y = -y * 0.5f + 0.5f;
 +
 +
// Show text (avoid back-projection)
 +
if( pos.w > 0 ) h3dutShowText( name, x, y, 0.02f, 1.0f, 1.0f, 1.0f, _fontMatRes);
 +
}
 +
</source>}}
 +
}}
 +
 +
 +
== How to tell the camera to look at a point ==
 +
 +
Only pseudo code here, how the math behind this works can be found by searching for gluLookAt.
 +
 +
{{CppSourceCode|description= Pseudo Code|code=
 +
<source lang="python" line="1">
 +
forward = targetPosition - cameraPosition
 +
forward = normalize(forward)
 +
 +
up = Vector3(0, 1, 0) # vector pointing "up", can be almost any vector
 +
up = normalize(up)
 +
 +
right = cross(forward, up)
 +
right = normalize(right)
 +
 +
up = cross(right, forward)
 +
 +
# rotation matrix, column major format
 +
m1 = [right.x, up.x, -forward.x, 0,
 +
    right.y, up.y, -forward.y, 0,
 +
    right.z, up.z, -forward.z, 0,
 +
    0, 0, 0, 1]
 +
 +
# translation matrix, column major format
 +
m2 = [1, 0, 0, 0,
 +
    0, 1, 0, 0,
 +
    0, 0, 1, 0,
 +
    -cameraPosition.x, -cameraPosition.y, -cameraPosition.z, 1]
 +
 +
# combined transformation matrix ("view" matrix)
 +
m3 = m1 * m2
 +
 +
# since the camera node is just another node in Horde3D we have to invert the matrix m3
 +
m3 = invert(m3)
 +
 +
h3dSetNodeTransMat(cameraNode, m3)
 +
 +
</source>}}
 +
This example will fail when either forward is a zero vector or forward is parallel to the choosen up vector.
 +
 +
 +
== How to orbit / rotate the camera around a point ==
 +
{{CppSourceCode|description= Pseudo Code|code=
 +
<source lang="python" line="1">
 +
# setup: create an anchor node, so that the camera can always rotate around a relative (0, 0, 0) point
 +
anchorNode = h3dAddGroupNode(h3dRootNode, 'camera-anchor')
 +
cameraNode = h3dAddCameraNode(anchorNode, 'camera-orbiter')
 +
 +
# basically calculate point on the surface of a sphere
 +
# by using distance and two angles
 +
 +
phi = phi % 360.0
 +
theta = theta % 360.0
 +
 +
if phi < 0: # will break for phi=0 and theta=0 otherwise
 +
    phi = 0.0001
 +
 +
r = distance
 +
p = radians(phi)
 +
t = radians(theta)
 +
 +
x = r * cos(t) * sin(p)
 +
y = r * cos(p)
 +
z = r * sin(t) * sin(p)
 +
 +
# calculate vectors for camera look at
 +
forward = Vector3(0, 0, 0,) - Vector3(x, y, z)
 +
if phi >= 180.0:
 +
    up = Vector3(0, 1, 0)
 +
else:
 +
    up = Vector3(0, -1, 0)
 +
 +
# with the forward and up vector use the code from the look at example to point the camera at the point (0, 0, 0).
 +
 +
 +
</source>}}
 +
When following another scene node use that node instead of h3dRootNode as a parent in the h3dAddGroupNode call.

Latest revision as of 15:19, 17 September 2010

The HOWTO contains quick answers for smaller problems.



How to enable anti-aliasing (MSAA)

Anti-aliasing can only be enabled for render targets. The RenderTarget element used in Pipeline resources has an attribute maxSamples which defines the maximum number of samples used for MSAA. The actual number of samples is controlled by the engine option SampleCount. This makes it possible to control the anti-aliasing quality from the application. If SampleCount is set to zero, MSAA is disabled. Note that the hardware needs to support the EXT_framebuffer_multisample extension in order for the MSAA to work.


How to get the size of a loaded texture

The following code queries the dimensions of the base mipmap of a loaded texture:

C++ Code
int width = h3dGetResParamI( texResHandle, H3DTexRes::ImageElem, 0, H3DTexRes::ImgWidthI );
int height = h3dGetResParamI( texResHandle, H3DTexRes::ImageElem, 0, H3DTexRes::ImgHeightI );

If you want to get the dimensions of mipmap level n, change the elemIdx parameter which is 0 above to n.


How to procedurally generate a texture

To generate a texture and bind it to a shader, you need to perform three steps:

First, create the texture handle, map data stream and write to it. Note that you need to perform this step before loading resources from disk (using H3dutLoadResourcesFromDisk())! Otherwise, the texture creation will fail.

C++ Code
H3DRes texRes = h3dCreateTexture( "myTexRes", 64, 64, H3DFormats::TEX_RGBA16F, H3DResFlags::NoTexMipmaps );
float *data = static_cast<float*>( h3dMapResStream( texRes, H3DTexRes::ImageElem, 0, H3DTexRes::ImgPixelStream, false, true ) );
for( int row=0; row<64; ++row )
{
    float *rowPtr = data + row*64*4;
    for( int col=0; col<64; ++col )
    {
        float *sample = rowPtr + col*4;
        sample[0] = ...; // R
        sample[1] = ...; // G
        sample[2] = ...; // B
        sample[3] = ...; // A
    }
}
h3dUnmapResStream( texRes );

Second, to bind the texture to a shader, you need to include it in the material XML file like this:

<Material>
    <Shader source="shaders/myshader.shader" />
    <Sampler name="myTexture" map="myTexRes" />
</Material>

Finally, to reference the sampler in the shader, you need to declare it like this:

Shader Code
[[FX]]
sampler myTexture;

// ... declare other samplers, contexts etc.

[[FS_LIGHTING]]

varying vec2 texCoords; // ... assumed given by vertex shader ...
uniform sampler2D myTexture;

void main( void )
{
  gl_FragColor = texture2D( myTexture, texCoords );
}

Information taken from this thread on the forums.


How to procedurally generate a geometry resource

To generate a geometry resource from given data call h3dutCreateGeometryRes:

the following example will create a simple 2D plane.

C++ Code
float posData[] = { 
  0,  0, 0, 
 10,  0, 0, 
  0, 10, 0,
 10, 10, 0
};

unsigned int indexData[] = { 0, 1, 2, 2, 1, 3 };
short normalData[] = {
 0, 0, 1,
 0, 0, 1,
 0, 0, 1,
 0, 0, 1
};

float uvData[] = {
 0, 0,
 1, 0,
 0, 1, 
 1, 1
};

H3DRes geoRes = h3dutCreateGeometryRes( "geoRes", 4, 6, posData, indexData, normalData, 0, 0, uvData, 0 );
H3DNode model = h3dAddModelNode( H3DRootNode, "DynGeoModelNode", geoRes );
h3dAddMeshNode( model, "DynGeoMesh", matRes, 0, 6, 0, 3 );


How to modify vertex data

Vertex data is stored as streams in geometry resources. A pointer to the vertex data can be obtained by mapping a stream. The following code modifies the y coordinates of all vertex positions stored in the specified geometry resource.

C++ Code
int vertCount = h3dGetResParamI( myGeoRes, H3DGeoRes::GeometryElem, 0, H3DGeoRes::GeoVertexCountI );
float *pVertPosData = (float *)h3dMapResStream( myGeoRes, H3DGeoRes::GeometryElem, 0, H3DGeoRes::GeoVertPosStream, true, true );

for( int i = 0; i < vertCount; ++i )
{
    pVertPosData[i * 3 + 1] = pVertPosData[i * 3 + 1] * 2;
}

h3dUnmapResStream( myGeoRes );


How to exchange a texture of a mesh

Consider you want to exchange the diffuse texture (sampler named albedoMap) of a given mesh (with node handle myMesh) by a texture resource with resource handle myNewTex. You can do that with the following code:

C++ Code
H3DRes matRes = h3dGetNodeParamI( myMesh, H3DMesh::MatResI );
int idx = h3dFindResElem( matRes, H3DMatRes::SamplerElem, H3DMatRes::SampNameStr, "albedoMap" );
h3dSetResParamI( matRes, H3DMatRes::SamplerElem, idx, H3DMatRes::SampTexResI, myNewTex );

First you need to retrieve the material used by the mesh. After that, you can find the desired sampler in the material. Finally, once you have the sampler index, you can access the sampler data and override its texture resource.

Note: The code above changes directly the material resource. If several meshes are using the same material, all of them will have the new texture. If that is not desired, you can create a private copy of the material by cloning it.


How to display text in the scene

Usually, text is displayed as part of a GUI or HUD on top of the scene. However, sometimes it can be useful to display text inside the scene at the location of a specific node. The following snippet displays the names of all models at the nodes' origins. For obtaining the right screen position, the node positions are projected and converted to overlay coordinates.

C++ Code
// Get camera view matrix
const float *camTrans;
h3dGetNodeTransMats( _cam, 0x0, &camTrans );
Matrix4f viewMat( Matrix4f( camTrans ).inverted() );

// Get camera projection matrix
float camProj[16];
h3dGetCameraProjMat( _cam, camProj );
Matrix4f projMat( camProj );

// Loop over all model nodes
int cnt = h3dFindNodes( H3DRootNode, "", H3DNodeTypes::Model );
for( int i = 0; i < cnt; ++i )
{
	// Get node name
	H3DNode node = h3dGetNodeFindResult( i );
	const char *name = h3dGetNodeParamStr( node, H3DNodeParams::NameStr );

	// Get node position
	const float *nodeTrans;
	h3dGetNodeTransMats( node, 0, &nodeTrans ); 
	Vec4f pos( nodeTrans[12], nodeTrans[13], nodeTrans[14], 1 );

	// Project
	pos = projMat * viewMat * pos;
	float x = pos.x / pos.w;
	float y = pos.y / pos.w;

	// Transform to overlay coordinates
    const float aspectRatio = h3dGetViewportParams( 0x0, 0x0, 0x0, 0x0 );
	x = (x * 0.5f + 0.5f)*aspectRatio;
	y = -y * 0.5f + 0.5f;

	// Show text (avoid back-projection)
	if( pos.w > 0 ) h3dutShowText( name, x, y, 0.02f, 1.0f, 1.0f, 1.0f, _fontMatRes);
}


How to tell the camera to look at a point

Only pseudo code here, how the math behind this works can be found by searching for gluLookAt.

Pseudo Code
forward = targetPosition - cameraPosition
forward = normalize(forward)

up = Vector3(0, 1, 0) # vector pointing "up", can be almost any vector
up = normalize(up)

right = cross(forward, up)
right = normalize(right)

up = cross(right, forward)

# rotation matrix, column major format
m1 = [right.x, up.x, -forward.x, 0,
    right.y, up.y, -forward.y, 0,
    right.z, up.z, -forward.z, 0,
    0, 0, 0, 1]

# translation matrix, column major format
m2 = [1, 0, 0, 0,
    0, 1, 0, 0,
    0, 0, 1, 0,
    -cameraPosition.x, -cameraPosition.y, -cameraPosition.z, 1]

# combined transformation matrix ("view" matrix)
m3 = m1 * m2

# since the camera node is just another node in Horde3D we have to invert the matrix m3
m3 = invert(m3)

h3dSetNodeTransMat(cameraNode, m3)

This example will fail when either forward is a zero vector or forward is parallel to the choosen up vector.


How to orbit / rotate the camera around a point

Pseudo Code
# setup: create an anchor node, so that the camera can always rotate around a relative (0, 0, 0) point
anchorNode = h3dAddGroupNode(h3dRootNode, 'camera-anchor')
cameraNode = h3dAddCameraNode(anchorNode, 'camera-orbiter')

# basically calculate point on the surface of a sphere
# by using distance and two angles

phi = phi % 360.0
theta = theta % 360.0

if phi < 0: # will break for phi=0 and theta=0 otherwise
    phi = 0.0001

r = distance
p = radians(phi)
t = radians(theta)

x = r * cos(t) * sin(p)
y = r * cos(p)
z = r * sin(t) * sin(p)

# calculate vectors for camera look at
forward = Vector3(0, 0, 0,) - Vector3(x, y, z)
if phi >= 180.0:
    up = Vector3(0, 1, 0)
else:
    up = Vector3(0, -1, 0)

# with the forward and up vector use the code from the look at example to point the camera at the point (0, 0, 0).

When following another scene node use that node instead of h3dRootNode as a parent in the h3dAddGroupNode call.