I'm a bit stuck on this, my normals/tangents/bitangents calculation works as follows:
- Unweld the model completely (no indices point to the same vertex).
- Create a map of indices with the same vertex.
- Calculate normals/tangents/bitangents for each face and add to all matching indices from the map
- Weld all vertices with same coordinates, texture coordinates and normals.
Still parallax doesn't look correct (even if I skip welding). If I disable smoothing things look a bit better, but
not correct either.
I've made a little demo to demonstrate the problem:
http://corrado.bsdwebhosting.com/~jimbo/trouble.zipAlso I've added the same model converted to .obj then to Collada, and then with colladaconv to horde3d, which looks correct.
The problem part must be in these functions:
Code:
# find vertices with same coordinates, this is for normal smoothing, return of list of matching indices
sub find_same_vertices {
my $object = shift;
my $indices_map = shift;
my $index = shift;
my $vertex = $object->{numvert}->[$index];
return @{ $indices_map->{ $vertex->as_string } };
}
# create map of indices with same vertices
sub make_indices_map {
my $object = shift;
my %indices_map;
foreach my $surface ( @{ $object->{numsurf} } ) {
# only average out smooth vertex normals
next if ( $surface->{SURF}->{shading} ne 'smooth' );
foreach my $ref ( @{ $surface->{refs} } ) {
my $v = $object->{numvert}->[ $ref->{index} ];
push @{ $indices_map{ $v->as_string } }, $ref->{index};
}
}
return \%indices_map;
}
sub calc_vertex_normals {
my $object = shift;
warn "calculating normals: $object->{name}\n";
my @normals;
my @tangents;
my @bitangents;
my $indices_map = make_indices_map($object);
foreach my $surface ( @{ $object->{numsurf} } ) {
my $index0 = $surface->{refs}->[0]->{index};
my $index1 = $surface->{refs}->[1]->{index};
my $index2 = $surface->{refs}->[2]->{index};
my $v0 = $object->{numvert}->[$index0];
my $v1 = $object->{numvert}->[$index1];
my $v2 = $object->{numvert}->[$index2];
my $d0 = $v1 - $v0;
my $d1 = $v2 - $v0;
my $v = ( $d0 x $d1 )->normalized;
#
# Calculate the normal,tangent and bitangent for this face and add it
# to the running sum of normals for each of the
# vertices involved
#
my $Edge0uv = uv_sub( $surface->{refs}->[1], $surface->{refs}->[0] );
my $Edge1uv = uv_sub( $surface->{refs}->[2], $surface->{refs}->[0] );
my $cp = $Edge0uv->{u} * $Edge1uv->{v} - $Edge1uv->{u} * $Edge0uv->{v};
my $r = 0;
if ( $cp != 0 ) {
$r = 1.0 / $cp;
}
my $tangent = ( $d0 * $Edge1uv->{v} - $d1 * $Edge0uv->{v} ) * $r;
my $bitangent = ( $d1 * $Edge0uv->{u} - $d0 * $Edge1uv->{u} ) * $r;
my @indices;
push @indices, find_same_vertices( $object, $indices_map, $index0 );
push @indices, find_same_vertices( $object, $indices_map, $index1 );
push @indices, find_same_vertices( $object, $indices_map, $index2 );
foreach my $index (@indices) {
$normals[$index] ||= vec3();
$tangents[$index] ||= vec3();
$bitangents[$index] ||= vec3();
$normals[$index] += $v;
$tangents[$index] += $tangent;
$bitangents[$index] += $bitangent;
}
}
# Normalize and fixup the vertex normal vectors, tangents en bitangents
my $numVerts = int( @{ $object->{numvert} } );
for ( my $j = 0 ; $j < $numVerts ; $j++ ) {
my $n = $normals[$j]->normalized;
my $t = $tangents[$j];
# orthogonalize
$tangents[$j] = ( $t - $n * ( $n * $t ) )->normalized;
$normals[$j] = $n;
if ( ( $n x $t ) * $bitangents[$j] < 0 ) {
$bitangents[$j] = ( $n * -1 ) x $t;
}
else {
$bitangents[$j] = $n x $t;
}
}
$object->{normals} = \@normals;
$object->{tangents} = \@tangents;
$object->{bitangents} = \@bitangents;
}