2013-08-23 00:26:16 +02:00
// Copyright (c) 2013- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
# include "TransformPipeline.h"
2013-09-24 11:14:04 +02:00
# include "Core/Config.h"
2013-08-23 00:26:16 +02:00
# include "Core/MemMap.h"
2013-09-21 23:37:14 +02:00
# include "GPU/Math3D.h"
2013-09-24 14:13:13 +02:00
# include "GPU/Common/SplineCommon.h"
2013-09-21 23:37:14 +02:00
2013-09-23 22:46:06 +02:00
// Here's how to evaluate them fast:
// http://and-what-happened.blogspot.se/2012/07/evaluating-b-splines-aka-basis-splines.html
2013-09-21 23:37:14 +02:00
// This normalizes a set of vertices in any format to SimpleVertex format, by processing away morphing AND skinning.
// The rest of the transform pipeline like lighting will go as normal, either hardware or software.
// The implementation is initially a bit inefficient but shouldn't be a big deal.
// An intermediate buffer of not-easy-to-predict size is stored at bufPtr.
u32 TransformDrawEngine : : NormalizeVertices ( u8 * outPtr , u8 * bufPtr , const u8 * inPtr , int lowerBound , int upperBound , u32 vertType ) {
// First, decode the vertices into a GPU compatible format. This step can be eliminated but will need a separate
// implementation of the vertex decoder.
VertexDecoder * dec = GetVertexDecoder ( vertType ) ;
dec - > DecodeVerts ( bufPtr , inPtr , lowerBound , upperBound ) ;
// OK, morphing eliminated but bones still remain to be taken care of.
// Let's do a partial software transform where we only do skinning.
VertexReader reader ( bufPtr , dec - > GetDecVtxFmt ( ) , vertType ) ;
SimpleVertex * sverts = ( SimpleVertex * ) outPtr ;
2013-09-25 18:50:09 +02:00
const u8 defaultColor [ 4 ] = {
( u8 ) gstate . getMaterialAmbientR ( ) ,
( u8 ) gstate . getMaterialAmbientG ( ) ,
( u8 ) gstate . getMaterialAmbientB ( ) ,
( u8 ) gstate . getMaterialAmbientA ( ) ,
2013-09-21 23:37:14 +02:00
} ;
// Let's have two separate loops, one for non skinning and one for skinning.
2013-11-14 14:34:47 +01:00
if ( ! g_Config . bSoftwareSkinning & & ( vertType & GE_VTYPE_WEIGHT_MASK ) ! = GE_VTYPE_WEIGHT_NONE ) {
2013-09-21 23:37:14 +02:00
int numBoneWeights = vertTypeGetNumBoneWeights ( vertType ) ;
for ( int i = lowerBound ; i < = upperBound ; i + + ) {
reader . Goto ( i ) ;
SimpleVertex & sv = sverts [ i ] ;
if ( vertType & GE_VTYPE_TC_MASK ) {
reader . ReadUV ( sv . uv ) ;
}
if ( vertType & GE_VTYPE_COL_MASK ) {
reader . ReadColor0_8888 ( sv . color ) ;
} else {
memcpy ( sv . color , defaultColor , 4 ) ;
}
float nrm [ 3 ] , pos [ 3 ] ;
float bnrm [ 3 ] , bpos [ 3 ] ;
if ( vertType & GE_VTYPE_NRM_MASK ) {
// Normals are generated during tesselation anyway, not sure if any need to supply
reader . ReadNrm ( nrm ) ;
} else {
nrm [ 0 ] = 0 ;
nrm [ 1 ] = 0 ;
nrm [ 2 ] = 1.0f ;
}
reader . ReadPos ( pos ) ;
// Apply skinning transform directly
float weights [ 8 ] ;
reader . ReadWeights ( weights ) ;
// Skinning
Vec3f psum ( 0 , 0 , 0 ) ;
Vec3f nsum ( 0 , 0 , 0 ) ;
2013-10-05 10:16:06 -07:00
for ( int w = 0 ; w < numBoneWeights ; w + + ) {
if ( weights [ w ] ! = 0.0f ) {
Vec3ByMatrix43 ( bpos , pos , gstate . boneMatrix + w * 12 ) ;
2013-09-21 23:37:14 +02:00
Vec3f tpos ( bpos ) ;
2013-10-05 10:16:06 -07:00
psum + = tpos * weights [ w ] ;
2013-09-21 23:37:14 +02:00
2013-10-05 10:16:06 -07:00
Norm3ByMatrix43 ( bnrm , nrm , gstate . boneMatrix + w * 12 ) ;
2013-09-21 23:37:14 +02:00
Vec3f tnorm ( bnrm ) ;
2013-10-05 10:16:06 -07:00
nsum + = tnorm * weights [ w ] ;
2013-09-21 23:37:14 +02:00
}
}
2013-09-22 09:51:32 +02:00
sv . pos = psum ;
sv . nrm = nsum ;
2013-09-21 23:37:14 +02:00
}
} else {
for ( int i = lowerBound ; i < = upperBound ; i + + ) {
reader . Goto ( i ) ;
SimpleVertex & sv = sverts [ i ] ;
if ( vertType & GE_VTYPE_TC_MASK ) {
reader . ReadUV ( sv . uv ) ;
} else {
sv . uv [ 0 ] = 0 ; // This will get filled in during tesselation
sv . uv [ 1 ] = 0 ;
}
if ( vertType & GE_VTYPE_COL_MASK ) {
reader . ReadColor0_8888 ( sv . color ) ;
} else {
memcpy ( sv . color , defaultColor , 4 ) ;
}
if ( vertType & GE_VTYPE_NRM_MASK ) {
// Normals are generated during tesselation anyway, not sure if any need to supply
2013-09-22 09:51:32 +02:00
reader . ReadNrm ( ( float * ) & sv . nrm ) ;
2013-09-21 23:37:14 +02:00
} else {
2013-09-22 09:51:32 +02:00
sv . nrm . x = 0 ;
sv . nrm . y = 0 ;
sv . nrm . z = 1.0f ;
2013-09-21 23:37:14 +02:00
}
2013-09-22 09:51:32 +02:00
reader . ReadPos ( ( float * ) & sv . pos ) ;
2013-09-21 23:37:14 +02:00
}
}
// Okay, there we are! Return the new type (but keep the index bits)
return GE_VTYPE_TC_FLOAT | GE_VTYPE_COL_8888 | GE_VTYPE_NRM_FLOAT | GE_VTYPE_POS_FLOAT | ( vertType & GE_VTYPE_IDX_MASK ) ;
}
2013-09-23 18:33:13 +02:00
# define START_OPEN 1
# define END_OPEN 2
2013-08-23 00:26:16 +02:00
2013-09-23 15:55:07 +02:00
static float lerp ( float a , float b , float x ) {
return a + x * ( b - a ) ;
}
static void lerpColor ( u8 a [ 4 ] , u8 b [ 4 ] , float x , u8 out [ 4 ] ) {
for ( int i = 0 ; i < 4 ; i + + ) {
out [ i ] = ( float ) a [ i ] + x * ( ( float ) b [ i ] - ( float ) a [ i ] ) ;
}
}
2013-08-23 00:26:16 +02:00
// We decode all vertices into a common format for easy interpolation and stuff.
2013-08-30 10:25:01 +02:00
// Not fast but can be optimized later.
2013-09-23 18:33:13 +02:00
struct BezierPatch {
2013-09-21 23:37:14 +02:00
SimpleVertex * points [ 16 ] ;
2013-08-30 10:25:01 +02:00
2013-09-22 09:51:32 +02:00
// These are used to generate UVs.
int u_index , v_index ;
2013-09-23 15:55:07 +02:00
// Interpolate colors between control points (bilinear, should be good enough).
void sampleColor ( float u , float v , u8 color [ 4 ] ) const {
u * = 3.0f ;
v * = 3.0f ;
int iu = ( int ) floorf ( u ) ;
int iv = ( int ) floorf ( v ) ;
int iu2 = iu + 1 ;
int iv2 = iv + 1 ;
float fracU = u - iu ;
float fracV = v - iv ;
if ( iu2 > 3 ) iu2 = 3 ;
if ( iv2 > 3 ) iv2 = 3 ;
int tl = iu + 4 * iv ;
int tr = iu2 + 4 * iv ;
int bl = iu + 4 * iv2 ;
int br = iu2 + 4 * iv2 ;
u8 upperColor [ 4 ] , lowerColor [ 4 ] ;
lerpColor ( points [ tl ] - > color , points [ tr ] - > color , fracU , upperColor ) ;
lerpColor ( points [ bl ] - > color , points [ br ] - > color , fracU , lowerColor ) ;
lerpColor ( upperColor , lowerColor , fracV , color ) ;
}
2013-10-10 17:36:42 +02:00
2013-09-23 15:55:07 +02:00
void sampleTexUV ( float u , float v , float & tu , float & tv ) const {
u * = 3.0f ;
v * = 3.0f ;
int iu = ( int ) floorf ( u ) ;
int iv = ( int ) floorf ( v ) ;
int iu2 = iu + 1 ;
int iv2 = iv + 1 ;
float fracU = u - iu ;
float fracV = v - iv ;
if ( iu2 > 3 ) iu2 = 3 ;
if ( iv2 > 3 ) iv2 = 3 ;
int tl = iu + 4 * iv ;
int tr = iu2 + 4 * iv ;
int bl = iu + 4 * iv2 ;
int br = iu2 + 4 * iv2 ;
float upperTU = lerp ( points [ tl ] - > uv [ 0 ] , points [ tr ] - > uv [ 0 ] , fracU ) ;
float upperTV = lerp ( points [ tl ] - > uv [ 1 ] , points [ tr ] - > uv [ 1 ] , fracU ) ;
float lowerTU = lerp ( points [ bl ] - > uv [ 0 ] , points [ br ] - > uv [ 0 ] , fracU ) ;
float lowerTV = lerp ( points [ bl ] - > uv [ 1 ] , points [ br ] - > uv [ 1 ] , fracU ) ;
tu = lerp ( upperTU , lowerTU , fracV ) ;
tv = lerp ( upperTV , lowerTV , fracV ) ;
}
2013-08-23 00:26:16 +02:00
} ;
2013-09-23 18:33:13 +02:00
struct SplinePatch {
SimpleVertex * * points ;
int count_u ;
int count_v ;
int type_u ;
int type_v ;
/*
// Interpolate colors between control points (bilinear, should be good enough).
void sampleColor ( float u , float v , u8 color [ 4 ] ) const {
u * = 3.0f ;
v * = 3.0f ;
int iu = ( int ) floorf ( u ) ;
int iv = ( int ) floorf ( v ) ;
int iu2 = iu + 1 ;
int iv2 = iv + 1 ;
float fracU = u - iu ;
float fracV = v - iv ;
if ( iu2 > = count_u ) iu2 = count_u - 1 ;
if ( iv2 > = count_v ) iv2 = count_v - 1 ;
int tl = iu + count_u * iv ;
int tr = iu2 + count_u * iv ;
int bl = iu + count_u * iv2 ;
int br = iu2 + count_u * iv2 ;
u8 upperColor [ 4 ] , lowerColor [ 4 ] ;
lerpColor ( points [ tl ] - > color , points [ tr ] - > color , fracU , upperColor ) ;
lerpColor ( points [ bl ] - > color , points [ br ] - > color , fracU , lowerColor ) ;
lerpColor ( upperColor , lowerColor , fracV , color ) ;
}
void sampleTexUV ( float u , float v , float & tu , float & tv ) const {
u * = 3.0f ;
v * = 3.0f ;
int iu = ( int ) floorf ( u ) ;
int iv = ( int ) floorf ( v ) ;
int iu2 = iu + 1 ;
int iv2 = iv + 1 ;
float fracU = u - iu ;
float fracV = v - iv ;
if ( iu2 > = count_u ) iu2 = count_u - 1 ;
if ( iv2 > = count_v ) iv2 = count_v - 1 ;
int tl = iu + count_u * iv ;
int tr = iu2 + count_u * iv ;
int bl = iu + count_u * iv2 ;
int br = iu2 + count_u * iv2 ;
float upperTU = lerp ( points [ tl ] - > uv [ 0 ] , points [ tr ] - > uv [ 0 ] , fracU ) ;
float upperTV = lerp ( points [ tl ] - > uv [ 1 ] , points [ tr ] - > uv [ 1 ] , fracU ) ;
float lowerTU = lerp ( points [ bl ] - > uv [ 0 ] , points [ br ] - > uv [ 0 ] , fracU ) ;
float lowerTV = lerp ( points [ bl ] - > uv [ 1 ] , points [ br ] - > uv [ 1 ] , fracU ) ;
tu = lerp ( upperTU , lowerTU , fracV ) ;
tv = lerp ( upperTV , lowerTV , fracV ) ;
} */
} ;
2013-09-24 11:14:04 +02:00
static void CopyQuad ( u8 * & dest , const SimpleVertex * v1 , const SimpleVertex * v2 , const SimpleVertex * v3 , const SimpleVertex * v4 ) {
2013-09-21 23:37:14 +02:00
int vertexSize = sizeof ( SimpleVertex ) ;
2013-08-23 00:26:16 +02:00
memcpy ( dest , v1 , vertexSize ) ;
dest + = vertexSize ;
memcpy ( dest , v2 , vertexSize ) ;
dest + = vertexSize ;
memcpy ( dest , v3 , vertexSize ) ;
dest + = vertexSize ;
2013-09-24 11:14:04 +02:00
memcpy ( dest , v4 , vertexSize ) ;
dest + = vertexSize ;
2013-08-23 00:26:16 +02:00
}
2013-09-23 15:55:07 +02:00
# undef b2
// Bernstein basis functions
inline float bern0 ( float x ) { return ( 1 - x ) * ( 1 - x ) * ( 1 - x ) ; }
inline float bern1 ( float x ) { return 3 * x * ( 1 - x ) * ( 1 - x ) ; }
inline float bern2 ( float x ) { return 3 * x * x * ( 1 - x ) ; }
inline float bern3 ( float x ) { return x * x * x ; }
// Not sure yet if these have any use
inline float bern0deriv ( float x ) { return - 3 * ( x - 1 ) * ( x - 1 ) ; }
inline float bern1deriv ( float x ) { return 9 * x * x - 12 * x + 3 ; }
inline float bern2deriv ( float x ) { return 3 * ( 2 - 3 * x ) * x ; }
inline float bern3deriv ( float x ) { return 3 * x * x ; }
2013-09-22 09:51:32 +02:00
// http://en.wikipedia.org/wiki/Bernstein_polynomial
2013-09-23 15:55:07 +02:00
Vec3f Bernstein3D ( const Vec3f p0 , const Vec3f p1 , const Vec3f p2 , const Vec3f p3 , float x ) {
return p0 * bern0 ( x ) + p1 * bern1 ( x ) + p2 * bern2 ( x ) + p3 * bern3 ( x ) ;
2013-09-22 09:51:32 +02:00
}
2013-09-23 15:55:07 +02:00
Vec3f Bernstein3DDerivative ( const Vec3f p0 , const Vec3f p1 , const Vec3f p2 , const Vec3f p3 , float x ) {
return p0 * bern0deriv ( x ) + p1 * bern1deriv ( x ) + p2 * bern2deriv ( x ) + p3 * bern3deriv ( x ) ;
}
2013-09-22 09:51:32 +02:00
2013-09-24 11:14:04 +02:00
void spline_n_4 ( int i , float t , float * knot , float * splineVal ) {
2013-09-23 22:46:06 +02:00
knot + = i + 1 ;
float t0 = ( t - knot [ 0 ] ) ;
float t1 = ( t - knot [ 1 ] ) ;
float t2 = ( t - knot [ 2 ] ) ;
float f30 = t0 / ( knot [ 3 ] - knot [ 0 ] ) ;
float f41 = t1 / ( knot [ 4 ] - knot [ 1 ] ) ;
float f52 = t2 / ( knot [ 5 ] - knot [ 2 ] ) ;
float f31 = t1 / ( knot [ 3 ] - knot [ 1 ] ) ;
float f42 = t2 / ( knot [ 4 ] - knot [ 2 ] ) ;
float f32 = t2 / ( knot [ 3 ] - knot [ 2 ] ) ;
float a = ( 1 - f30 ) * ( 1 - f31 ) ;
float b = ( f31 * f41 ) ;
float c = ( 1 - f41 ) * ( 1 - f42 ) ;
float d = ( f42 * f52 ) ;
splineVal [ 0 ] = a - ( a * f32 ) ;
splineVal [ 1 ] = 1 - a - b + ( ( a + b + c - 1 ) * f32 ) ;
splineVal [ 2 ] = b + ( ( 1 - b - c - d ) * f32 ) ;
splineVal [ 3 ] = d * f32 ;
2013-09-23 18:33:13 +02:00
}
2013-09-24 11:14:04 +02:00
// knot should be an array sized n + 5 (n + 1 + 1 + degree (cubic))
void spline_knot ( int n , int type , float * knot ) {
memset ( knot , 0 , sizeof ( float ) * ( n + 5 ) ) ;
for ( int i = 0 ; i < n - 1 ; + + i )
2013-09-23 18:33:13 +02:00
knot [ i + 3 ] = i ;
if ( ( type & 1 ) = = 0 ) {
knot [ 0 ] = - 3 ;
knot [ 1 ] = - 2 ;
2013-10-10 17:36:42 +02:00
knot [ 2 ] = - 1 ;
2013-09-23 18:33:13 +02:00
}
if ( ( type & 2 ) = = 0 ) {
knot [ n + 2 ] = n - 1 ;
knot [ n + 3 ] = n ;
knot [ n + 4 ] = n + 1 ;
} else {
knot [ n + 2 ] = n - 2 ;
knot [ n + 3 ] = n - 2 ;
knot [ n + 4 ] = n - 2 ;
}
}
void TesselateSplinePatch ( u8 * & dest , int & count , const SplinePatch & spatch , u32 origVertType ) {
2013-09-23 15:55:07 +02:00
const float third = 1.0f / 3.0f ;
2013-09-24 11:14:04 +02:00
if ( g_Config . bLowQualitySplineBezier ) {
2013-09-23 18:33:13 +02:00
// Fast and easy way - just draw the control points, generate some very basic normal vector substitutes.
// Very inaccurate but okay for Loco Roco. Maybe should keep it as an option because it's fast.
const int tile_min_u = ( spatch . type_u & START_OPEN ) ? 0 : 1 ;
const int tile_min_v = ( spatch . type_v & START_OPEN ) ? 0 : 1 ;
const int tile_max_u = ( spatch . type_u & END_OPEN ) ? spatch . count_u - 1 : spatch . count_u - 2 ;
const int tile_max_v = ( spatch . type_v & END_OPEN ) ? spatch . count_v - 1 : spatch . count_v - 2 ;
for ( int tile_v = tile_min_v ; tile_v < tile_max_v ; + + tile_v ) {
for ( int tile_u = tile_min_u ; tile_u < tile_max_u ; + + tile_u ) {
int point_index = tile_u + tile_v * spatch . count_u ;
SimpleVertex v0 = * spatch . points [ point_index ] ;
SimpleVertex v1 = * spatch . points [ point_index + 1 ] ;
SimpleVertex v2 = * spatch . points [ point_index + spatch . count_u ] ;
SimpleVertex v3 = * spatch . points [ point_index + spatch . count_u + 1 ] ;
// Generate UV. TODO: Do this even if UV specified in control points?
if ( ( origVertType & GE_VTYPE_TC_MASK ) = = 0 ) {
float u = tile_u * third ;
float v = tile_v * third ;
v0 . uv [ 0 ] = u ;
v0 . uv [ 1 ] = v ;
v1 . uv [ 0 ] = u + third ;
v1 . uv [ 1 ] = v ;
v2 . uv [ 0 ] = u ;
v2 . uv [ 1 ] = v + third ;
v3 . uv [ 0 ] = u + third ;
v3 . uv [ 1 ] = v + third ;
}
// Generate normal if lighting is enabled (otherwise there's no point).
// This is a really poor quality algorithm, we get facet normals.
if ( gstate . isLightingEnabled ( ) ) {
Vec3f norm = Cross ( v1 . pos - v0 . pos , v2 . pos - v0 . pos ) ;
norm . Normalize ( ) ;
if ( gstate . patchfacing & 1 )
norm * = - 1.0f ;
v0 . nrm = norm ;
v1 . nrm = norm ;
v2 . nrm = norm ;
v3 . nrm = norm ;
}
2013-09-24 11:14:04 +02:00
CopyQuad ( dest , & v0 , & v1 , & v2 , & v3 ) ;
2013-09-23 18:33:13 +02:00
count + = 6 ;
}
}
} else {
// Full correct tessellation of spline patches.
// Does not yet generate normals and is atrociously slow (see spline_s...)
// First, generate knot vectors.
int n = spatch . count_u - 1 ;
int m = spatch . count_v - 1 ;
2013-09-24 11:14:04 +02:00
float * knot_u = new float [ n + 5 ] ;
float * knot_v = new float [ m + 5 ] ;
2013-09-23 18:33:13 +02:00
spline_knot ( n , spatch . type_u , knot_u ) ;
spline_knot ( m , spatch . type_v , knot_v ) ;
int patch_div_s = gstate . getPatchDivisionU ( ) ;
int patch_div_t = gstate . getPatchDivisionV ( ) ;
// Increase tesselation based on the size. Should be approximately right?
// JPCSP is wrong at least because their method results in square loco roco.
patch_div_s = ( spatch . count_u - 3 ) * patch_div_s / 3 ;
patch_div_t = ( spatch . count_v - 3 ) * patch_div_t / 3 ;
if ( patch_div_s = = 0 ) patch_div_s = 1 ;
if ( patch_div_t = = 0 ) patch_div_t = 1 ;
// TODO: Remove this cap when spline_s has been optimized.
2013-09-24 11:14:04 +02:00
if ( patch_div_s > 64 ) patch_div_s = 64 ;
if ( patch_div_t > 64 ) patch_div_t = 64 ;
2013-09-23 18:33:13 +02:00
// First compute all the vertices and put them in an array
SimpleVertex * vertices = new SimpleVertex [ ( patch_div_s + 1 ) * ( patch_div_t + 1 ) ] ;
2013-09-24 14:13:13 +02:00
float tu_width = 1.0f + ( spatch . count_u - 4 ) * 1.0f / 3.0f ;
float tv_height = 1.0f + ( spatch . count_v - 4 ) * 1.0f / 3.0f ;
2013-09-23 18:33:13 +02:00
bool computeNormals = gstate . isLightingEnabled ( ) ;
for ( int tile_v = 0 ; tile_v < patch_div_t + 1 ; tile_v + + ) {
float v = ( ( float ) tile_v * ( float ) ( m - 2 ) / ( float ) ( patch_div_t + 0.00001f ) ) ; // epsilon to prevent division by 0 in spline_s
for ( int tile_u = 0 ; tile_u < patch_div_s + 1 ; tile_u + + ) {
float u = ( ( float ) tile_u * ( float ) ( n - 2 ) / ( float ) ( patch_div_s + 0.00001f ) ) ;
SimpleVertex * vert = & vertices [ tile_v * ( patch_div_s + 1 ) + tile_u ] ;
vert - > pos . SetZero ( ) ;
if ( origVertType & GE_VTYPE_NRM_MASK ) {
vert - > nrm . SetZero ( ) ;
} else {
vert - > nrm . SetZero ( ) ;
vert - > nrm . z = 1.0f ;
}
if ( origVertType & GE_VTYPE_COL_MASK ) {
memset ( vert - > color , 0 , 4 ) ;
} else {
memcpy ( vert - > color , spatch . points [ 0 ] - > color , 4 ) ;
}
if ( origVertType & GE_VTYPE_TC_MASK ) {
vert - > uv [ 0 ] = 0.0f ;
vert - > uv [ 1 ] = 0.0f ;
} else {
2013-09-24 14:13:13 +02:00
vert - > uv [ 0 ] = tu_width * ( ( float ) tile_u / ( float ) patch_div_s ) ;
vert - > uv [ 1 ] = tv_height * ( ( float ) tile_v / ( float ) patch_div_t ) ;
2013-09-23 18:33:13 +02:00
}
// Collect influences from surrounding control points.
2013-09-23 22:46:06 +02:00
float u_weights [ 4 ] ;
float v_weights [ 4 ] ;
2013-10-10 17:36:42 +02:00
2013-09-23 22:46:06 +02:00
int iu = ( int ) u ;
int iv = ( int ) v ;
spline_n_4 ( iu , u , knot_u , u_weights ) ;
spline_n_4 ( iv , v , knot_v , v_weights ) ;
for ( int ii = 0 ; ii < 4 ; + + ii ) {
for ( int jj = 0 ; jj < 4 ; + + jj ) {
float u_spline = u_weights [ ii ] ;
float v_spline = v_weights [ jj ] ;
2013-09-23 18:33:13 +02:00
float f = u_spline * v_spline ;
2013-10-10 17:36:42 +02:00
2013-09-23 18:33:13 +02:00
if ( f > 0.0f ) {
2013-09-23 22:46:06 +02:00
SimpleVertex * a = spatch . points [ spatch . count_u * ( iv + jj ) + ( iu + ii ) ] ;
2013-09-23 18:33:13 +02:00
vert - > pos + = a - > pos * f ;
if ( origVertType & GE_VTYPE_TC_MASK ) {
vert - > uv [ 0 ] + = a - > uv [ 0 ] * f ;
vert - > uv [ 1 ] + = a - > uv [ 1 ] * f ;
}
if ( origVertType & GE_VTYPE_COL_MASK ) {
vert - > color [ 0 ] + = a - > color [ 0 ] * f ;
vert - > color [ 1 ] + = a - > color [ 1 ] * f ;
vert - > color [ 2 ] + = a - > color [ 2 ] * f ;
vert - > color [ 3 ] + = a - > color [ 3 ] * f ;
}
if ( origVertType & GE_VTYPE_NRM_MASK ) {
vert - > nrm + = a - > nrm * f ;
}
}
}
}
if ( origVertType & GE_VTYPE_NRM_MASK ) {
vert - > nrm . Normalize ( ) ;
}
}
}
delete [ ] knot_u ;
delete [ ] knot_v ;
2013-09-24 11:14:04 +02:00
// Hacky normal generation through central difference.
if ( gstate . isLightingEnabled ( ) & & ( origVertType & GE_VTYPE_NRM_MASK ) = = 0 ) {
for ( int v = 0 ; v < patch_div_t + 1 ; v + + ) {
for ( int u = 0 ; u < patch_div_s + 1 ; u + + ) {
int l = std : : max ( 0 , u - 1 ) ;
int t = std : : max ( 0 , v - 1 ) ;
int r = std : : min ( patch_div_s , u + 1 ) ;
int b = std : : min ( patch_div_t , v + 1 ) ;
const Vec3f & right = vertices [ v * ( patch_div_s + 1 ) + r ] . pos - vertices [ v * ( patch_div_s + 1 ) + l ] . pos ;
const Vec3f & down = vertices [ b * ( patch_div_s + 1 ) + u ] . pos - vertices [ t * ( patch_div_s + 1 ) + u ] . pos ;
vertices [ v * ( patch_div_s + 1 ) + u ] . nrm = Cross ( right , down ) . Normalized ( ) ;
if ( gstate . patchfacing & 1 ) {
vertices [ v * ( patch_div_s + 1 ) + u ] . nrm * = - 1.0f ;
}
}
}
}
2013-09-23 18:33:13 +02:00
// Tesselate. TODO: Use indices so we only need to emit 4 vertices per pair of triangles instead of six.
for ( int tile_v = 0 ; tile_v < patch_div_t ; + + tile_v ) {
for ( int tile_u = 0 ; tile_u < patch_div_s ; + + tile_u ) {
float u = ( ( float ) tile_u / ( float ) patch_div_s ) ;
float v = ( ( float ) tile_v / ( float ) patch_div_t ) ;
SimpleVertex * v0 = & vertices [ tile_v * ( patch_div_s + 1 ) + tile_u ] ;
SimpleVertex * v1 = & vertices [ tile_v * ( patch_div_s + 1 ) + tile_u + 1 ] ;
SimpleVertex * v2 = & vertices [ ( tile_v + 1 ) * ( patch_div_s + 1 ) + tile_u ] ;
SimpleVertex * v3 = & vertices [ ( tile_v + 1 ) * ( patch_div_s + 1 ) + tile_u + 1 ] ;
2013-09-24 11:14:04 +02:00
CopyQuad ( dest , v0 , v1 , v2 , v3 ) ;
2013-09-23 18:33:13 +02:00
count + = 6 ;
}
}
delete [ ] vertices ;
}
}
2013-10-10 17:36:42 +02:00
void TesselateBezierPatch ( u8 * & dest , int & count , int tess_u , int tess_v , const BezierPatch & patch , u32 origVertType ) {
2013-09-23 18:33:13 +02:00
const float third = 1.0f / 3.0f ;
2013-09-24 11:14:04 +02:00
if ( g_Config . bLowQualitySplineBezier ) {
2013-09-23 15:55:07 +02:00
// Fast and easy way - just draw the control points, generate some very basic normal vector subsitutes.
// Very inaccurate though but okay for Loco Roco. Maybe should keep it as an option.
2013-09-22 09:51:32 +02:00
float u_base = patch . u_index / 3.0f ;
float v_base = patch . v_index / 3.0f ;
2013-09-23 18:33:13 +02:00
for ( int tile_v = 0 ; tile_v < 3 ; tile_v + + ) {
for ( int tile_u = 0 ; tile_u < 3 ; tile_u + + ) {
2013-09-23 15:55:07 +02:00
int point_index = tile_u + tile_v * 4 ;
2013-09-22 09:51:32 +02:00
SimpleVertex v0 = * patch . points [ point_index ] ;
SimpleVertex v1 = * patch . points [ point_index + 1 ] ;
SimpleVertex v2 = * patch . points [ point_index + 4 ] ;
SimpleVertex v3 = * patch . points [ point_index + 5 ] ;
// Generate UV. TODO: Do this even if UV specified in control points?
2013-09-22 10:50:33 +02:00
if ( ( origVertType & GE_VTYPE_TC_MASK ) = = 0 ) {
2013-09-22 10:21:15 +02:00
float u = u_base + tile_u * third ;
float v = v_base + tile_v * third ;
v0 . uv [ 0 ] = u ;
v0 . uv [ 1 ] = v ;
v1 . uv [ 0 ] = u + third ;
v1 . uv [ 1 ] = v ;
v2 . uv [ 0 ] = u ;
v2 . uv [ 1 ] = v + third ;
v3 . uv [ 0 ] = u + third ;
v3 . uv [ 1 ] = v + third ;
}
2013-09-22 09:51:32 +02:00
// Generate normal if lighting is enabled (otherwise there's no point).
// This is a really poor quality algorithm, we get facet normals.
if ( gstate . isLightingEnabled ( ) ) {
2013-09-22 10:01:46 +02:00
Vec3f norm = Cross ( v1 . pos - v0 . pos , v2 . pos - v0 . pos ) ;
norm . Normalize ( ) ;
if ( gstate . patchfacing & 1 )
norm * = - 1.0f ;
v0 . nrm = norm ;
v1 . nrm = norm ;
v2 . nrm = norm ;
v3 . nrm = norm ;
2013-09-22 09:51:32 +02:00
}
2013-09-24 11:14:04 +02:00
CopyQuad ( dest , & v0 , & v1 , & v2 , & v3 ) ;
2013-09-22 09:51:32 +02:00
count + = 6 ;
}
}
} else {
2013-09-23 18:33:13 +02:00
// Full correct tesselation of bezier patches.
// Note: Does not handle splines correctly.
2013-09-22 09:51:32 +02:00
2013-09-23 15:55:07 +02:00
// First compute all the vertices and put them in an array
SimpleVertex * vertices = new SimpleVertex [ ( tess_u + 1 ) * ( tess_v + 1 ) ] ;
2013-09-22 09:51:32 +02:00
2013-09-24 11:14:04 +02:00
Vec3f * horiz = new Vec3f [ ( tess_u + 1 ) * 4 ] ;
Vec3f * horiz2 = horiz + ( tess_u + 1 ) * 1 ;
Vec3f * horiz3 = horiz + ( tess_u + 1 ) * 2 ;
Vec3f * horiz4 = horiz + ( tess_u + 1 ) * 3 ;
// Precompute the horizontal curves to we only have to evaluate the vertical ones.
for ( int i = 0 ; i < tess_u + 1 ; i + + ) {
float u = ( ( float ) i / ( float ) tess_u ) ;
horiz [ i ] = Bernstein3D ( patch . points [ 0 ] - > pos , patch . points [ 1 ] - > pos , patch . points [ 2 ] - > pos , patch . points [ 3 ] - > pos , u ) ;
horiz2 [ i ] = Bernstein3D ( patch . points [ 4 ] - > pos , patch . points [ 5 ] - > pos , patch . points [ 6 ] - > pos , patch . points [ 7 ] - > pos , u ) ;
horiz3 [ i ] = Bernstein3D ( patch . points [ 8 ] - > pos , patch . points [ 9 ] - > pos , patch . points [ 10 ] - > pos , patch . points [ 11 ] - > pos , u ) ;
horiz4 [ i ] = Bernstein3D ( patch . points [ 12 ] - > pos , patch . points [ 13 ] - > pos , patch . points [ 14 ] - > pos , patch . points [ 15 ] - > pos , u ) ;
}
2013-09-23 15:55:07 +02:00
bool computeNormals = gstate . isLightingEnabled ( ) ;
2013-09-24 11:14:04 +02:00
2013-09-22 09:51:32 +02:00
for ( int tile_v = 0 ; tile_v < tess_v + 1 ; + + tile_v ) {
for ( int tile_u = 0 ; tile_u < tess_u + 1 ; + + tile_u ) {
float u = ( ( float ) tile_u / ( float ) tess_u ) ;
float v = ( ( float ) tile_v / ( float ) tess_v ) ;
2013-09-23 15:55:07 +02:00
float bu = u ;
float bv = v ;
2013-09-23 18:33:13 +02:00
2013-09-23 15:55:07 +02:00
// TODO: Should be able to precompute the four curves per U, then just Bernstein per V. Will benefit large tesselation factors.
2013-09-24 11:14:04 +02:00
const Vec3f & pos1 = horiz [ tile_u ] ;
const Vec3f & pos2 = horiz2 [ tile_u ] ;
const Vec3f & pos3 = horiz3 [ tile_u ] ;
const Vec3f & pos4 = horiz4 [ tile_u ] ;
2013-09-23 15:55:07 +02:00
SimpleVertex & vert = vertices [ tile_v * ( tess_u + 1 ) + tile_u ] ;
if ( computeNormals ) {
Vec3f derivU1 = Bernstein3DDerivative ( patch . points [ 0 ] - > pos , patch . points [ 1 ] - > pos , patch . points [ 2 ] - > pos , patch . points [ 3 ] - > pos , bu ) ;
Vec3f derivU2 = Bernstein3DDerivative ( patch . points [ 4 ] - > pos , patch . points [ 5 ] - > pos , patch . points [ 6 ] - > pos , patch . points [ 7 ] - > pos , bu ) ;
Vec3f derivU3 = Bernstein3DDerivative ( patch . points [ 8 ] - > pos , patch . points [ 9 ] - > pos , patch . points [ 10 ] - > pos , patch . points [ 11 ] - > pos , bu ) ;
Vec3f derivU4 = Bernstein3DDerivative ( patch . points [ 12 ] - > pos , patch . points [ 13 ] - > pos , patch . points [ 14 ] - > pos , patch . points [ 15 ] - > pos , bu ) ;
Vec3f derivU = Bernstein3D ( derivU1 , derivU2 , derivU3 , derivU4 , bv ) ;
Vec3f derivV = Bernstein3DDerivative ( pos1 , pos2 , pos3 , pos4 , bv ) ;
// TODO: Interpolate normals instead of generating them, if available?
2013-09-23 18:33:13 +02:00
vert . nrm = Cross ( derivU , derivV ) . Normalized ( ) ;
2013-09-23 15:55:07 +02:00
if ( gstate . patchfacing & 1 )
vert . nrm * = - 1.0f ;
} else {
vert . nrm . SetZero ( ) ;
}
2013-09-23 18:33:13 +02:00
2013-09-23 15:55:07 +02:00
vert . pos = Bernstein3D ( pos1 , pos2 , pos3 , pos4 , bv ) ;
2013-09-23 18:33:13 +02:00
2013-09-23 15:55:07 +02:00
if ( ( origVertType & GE_VTYPE_TC_MASK ) = = 0 ) {
// Generate texcoord
vert . uv [ 0 ] = u + patch . u_index * third ;
vert . uv [ 1 ] = v + patch . v_index * third ;
} else {
// Sample UV from control points
patch . sampleTexUV ( u , v , vert . uv [ 0 ] , vert . uv [ 1 ] ) ;
}
2013-09-22 09:51:32 +02:00
2013-09-23 15:55:07 +02:00
if ( origVertType & GE_VTYPE_COL_MASK ) {
patch . sampleColor ( u , v , vert . color ) ;
} else {
memcpy ( vert . color , patch . points [ 0 ] - > color , 4 ) ;
}
2013-09-22 09:51:32 +02:00
}
}
2013-09-24 11:14:04 +02:00
delete [ ] horiz ;
2013-09-22 09:51:32 +02:00
2013-09-23 15:55:07 +02:00
// Tesselate. TODO: Use indices so we only need to emit 4 vertices per pair of triangles instead of six.
2013-09-23 18:33:13 +02:00
for ( int tile_v = 0 ; tile_v < tess_v ; + + tile_v ) {
for ( int tile_u = 0 ; tile_u < tess_u ; + + tile_u ) {
2013-09-23 15:55:07 +02:00
float u = ( ( float ) tile_u / ( float ) tess_u ) ;
float v = ( ( float ) tile_v / ( float ) tess_v ) ;
2013-09-22 09:51:32 +02:00
2013-09-24 11:14:04 +02:00
const SimpleVertex * v0 = & vertices [ tile_v * ( tess_u + 1 ) + tile_u ] ;
const SimpleVertex * v1 = & vertices [ tile_v * ( tess_u + 1 ) + tile_u + 1 ] ;
const SimpleVertex * v2 = & vertices [ ( tile_v + 1 ) * ( tess_u + 1 ) + tile_u ] ;
const SimpleVertex * v3 = & vertices [ ( tile_v + 1 ) * ( tess_u + 1 ) + tile_u + 1 ] ;
2013-09-22 09:51:32 +02:00
2013-09-24 11:14:04 +02:00
CopyQuad ( dest , v0 , v1 , v2 , v3 ) ;
2013-09-23 15:55:07 +02:00
count + = 6 ;
}
}
2013-09-22 09:51:32 +02:00
2013-09-23 15:55:07 +02:00
delete [ ] vertices ;
2013-09-22 09:51:32 +02:00
}
}
void TransformDrawEngine : : SubmitSpline ( void * control_points , void * indices , int count_u , int count_v , int type_u , int type_v , GEPatchPrimType prim_type , u32 vertType ) {
2013-08-23 00:26:16 +02:00
Flush ( ) ;
if ( prim_type ! = GE_PATCHPRIM_TRIANGLES ) {
// Only triangles supported!
return ;
}
u16 index_lower_bound = 0 ;
u16 index_upper_bound = count_u * count_v - 1 ;
2013-09-22 09:51:32 +02:00
bool indices_16bit = ( vertType & GE_VTYPE_IDX_MASK ) = = GE_VTYPE_IDX_16BIT ;
const u8 * indices8 = ( const u8 * ) indices ;
const u16 * indices16 = ( const u16 * ) indices ;
2013-08-23 00:26:16 +02:00
if ( indices )
2013-09-22 09:51:32 +02:00
GetIndexBounds ( indices , count_u * count_v , vertType , & index_lower_bound , & index_upper_bound ) ;
2013-08-23 00:26:16 +02:00
2013-09-21 23:37:14 +02:00
// Simplify away bones and morph before proceeding
SimpleVertex * simplified_control_points = ( SimpleVertex * ) ( decoded + 65536 * 12 ) ;
u8 * temp_buffer = decoded + 65536 * 24 ;
2013-09-22 10:50:33 +02:00
u32 origVertType = vertType ;
2013-09-22 09:51:32 +02:00
vertType = NormalizeVertices ( ( u8 * ) simplified_control_points , temp_buffer , ( u8 * ) control_points , index_lower_bound , index_upper_bound , vertType ) ;
2013-09-21 23:37:14 +02:00
2013-09-22 09:51:32 +02:00
VertexDecoder * vdecoder = GetVertexDecoder ( vertType ) ;
2013-09-21 23:37:14 +02:00
int vertexSize = vdecoder - > VertexSize ( ) ;
if ( vertexSize ! = sizeof ( SimpleVertex ) ) {
2013-09-25 18:50:09 +02:00
ERROR_LOG ( G3D , " Something went really wrong, vertex size: %i vs %i " , vertexSize , ( int ) sizeof ( SimpleVertex ) ) ;
2013-09-21 23:37:14 +02:00
}
const DecVtxFormat & vtxfmt = vdecoder - > GetDecVtxFmt ( ) ;
2013-08-23 00:26:16 +02:00
// TODO: Do something less idiotic to manage this buffer
2013-09-23 18:33:13 +02:00
SimpleVertex * * points = new SimpleVertex * [ count_u * count_v ] ;
// Make an array of pointers to the control points, to get rid of indices.
for ( int idx = 0 ; idx < count_u * count_v ; idx + + ) {
if ( indices )
points [ idx ] = simplified_control_points + ( indices_16bit ? indices16 [ idx ] : indices8 [ idx ] ) ;
else
points [ idx ] = simplified_control_points + idx ;
2013-08-23 00:26:16 +02:00
}
2013-09-21 23:37:14 +02:00
u8 * decoded2 = decoded + 65536 * 36 ;
2013-08-23 00:26:16 +02:00
int count = 0 ;
u8 * dest = decoded2 ;
2013-09-23 18:33:13 +02:00
SplinePatch patch ;
patch . type_u = type_u ;
patch . type_v = type_v ;
patch . count_u = count_u ;
patch . count_v = count_v ;
patch . points = points ;
TesselateSplinePatch ( dest , count , patch , origVertType ) ;
delete [ ] points ;
2013-08-23 00:26:16 +02:00
2013-09-24 11:14:04 +02:00
u32 vertTypeWithIndex16 = ( vertType & ~ GE_VTYPE_IDX_MASK ) | GE_VTYPE_IDX_16BIT ;
2013-08-23 00:26:16 +02:00
2013-12-04 14:33:45 +01:00
UVScale prevUVScale ;
if ( g_Config . bPrescaleUV ) {
// We scaled during Normalize already so let's turn it off when drawing.
prevUVScale = gstate_c . uv ;
gstate_c . uv . uScale = 1.0f ;
gstate_c . uv . vScale = 1.0f ;
gstate_c . uv . uOff = 0 ;
gstate_c . uv . vOff = 0 ;
}
2013-11-14 14:03:03 +01:00
SubmitPrim ( decoded2 , quadIndices_ , GE_PRIM_TRIANGLES , count , vertTypeWithIndex16 , 0 ) ;
2013-12-04 14:33:45 +01:00
2013-08-23 00:26:16 +02:00
Flush ( ) ;
2013-12-04 14:33:45 +01:00
if ( g_Config . bPrescaleUV ) {
gstate_c . uv = prevUVScale ;
}
2013-08-29 23:19:38 +08:00
}
2013-08-30 10:25:01 +02:00
2013-09-22 09:51:32 +02:00
void TransformDrawEngine : : SubmitBezier ( void * control_points , void * indices , int count_u , int count_v , GEPatchPrimType prim_type , u32 vertType ) {
Flush ( ) ;
2013-08-30 10:25:01 +02:00
if ( prim_type ! = GE_PATCHPRIM_TRIANGLES ) {
// Only triangles supported!
return ;
}
2013-09-22 09:51:32 +02:00
u16 index_lower_bound = 0 ;
u16 index_upper_bound = count_u * count_v - 1 ;
bool indices_16bit = ( vertType & GE_VTYPE_IDX_MASK ) = = GE_VTYPE_IDX_16BIT ;
const u8 * indices8 = ( const u8 * ) indices ;
const u16 * indices16 = ( const u16 * ) indices ;
if ( indices )
GetIndexBounds ( indices , count_u * count_v , vertType , & index_lower_bound , & index_upper_bound ) ;
// Simplify away bones and morph before proceeding
SimpleVertex * simplified_control_points = ( SimpleVertex * ) ( decoded + 65536 * 12 ) ;
u8 * temp_buffer = decoded + 65536 * 24 ;
2013-09-22 10:50:33 +02:00
u32 origVertType = vertType ;
2013-09-22 09:51:32 +02:00
vertType = NormalizeVertices ( ( u8 * ) simplified_control_points , temp_buffer , ( u8 * ) control_points , index_lower_bound , index_upper_bound , vertType ) ;
VertexDecoder * vdecoder = GetVertexDecoder ( vertType ) ;
2013-08-30 10:25:01 +02:00
2013-09-22 09:51:32 +02:00
int vertexSize = vdecoder - > VertexSize ( ) ;
if ( vertexSize ! = sizeof ( SimpleVertex ) ) {
2013-09-25 18:50:09 +02:00
ERROR_LOG ( G3D , " Something went really wrong, vertex size: %i vs %i " , vertexSize , ( int ) sizeof ( SimpleVertex ) ) ;
2013-09-22 09:51:32 +02:00
}
const DecVtxFormat & vtxfmt = vdecoder - > GetDecVtxFmt ( ) ;
// Bezier patches share less control points than spline patches. Otherwise they are pretty much the same (except bezier don't support the open/close thing)
int num_patches_u = ( count_u - 1 ) / 3 ;
int num_patches_v = ( count_v - 1 ) / 3 ;
2013-09-23 18:33:13 +02:00
BezierPatch * patches = new BezierPatch [ num_patches_u * num_patches_v ] ;
2013-09-22 09:51:32 +02:00
for ( int patch_u = 0 ; patch_u < num_patches_u ; patch_u + + ) {
for ( int patch_v = 0 ; patch_v < num_patches_v ; patch_v + + ) {
2013-09-23 18:33:13 +02:00
BezierPatch & patch = patches [ patch_u + patch_v * num_patches_u ] ;
2013-09-22 09:51:32 +02:00
for ( int point = 0 ; point < 16 ; + + point ) {
int idx = ( patch_u * 3 + point % 4 ) + ( patch_v * 3 + point / 4 ) * count_u ;
if ( indices )
patch . points [ point ] = simplified_control_points + ( indices_16bit ? indices16 [ idx ] : indices8 [ idx ] ) ;
else
patch . points [ point ] = simplified_control_points + idx ;
}
patch . u_index = patch_u * 3 ;
patch . v_index = patch_v * 3 ;
}
}
u8 * decoded2 = decoded + 65536 * 36 ;
int count = 0 ;
u8 * dest = decoded2 ;
2013-10-10 17:36:42 +02:00
// Simple approximation of the real tesselation factor.
// We shouldn't really split up into separate 4x4 patches, instead we should do something that works
// like the splines, so we subdivide across the whole "mega-patch".
int tess_u = gstate . getPatchDivisionU ( ) / num_patches_u ;
int tess_v = gstate . getPatchDivisionV ( ) / num_patches_v ;
if ( tess_u < 4 ) tess_u = 4 ;
if ( tess_v < 4 ) tess_v = 4 ;
2013-09-22 09:51:32 +02:00
for ( int patch_idx = 0 ; patch_idx < num_patches_u * num_patches_v ; + + patch_idx ) {
2013-09-23 18:33:13 +02:00
BezierPatch & patch = patches [ patch_idx ] ;
2013-10-10 17:36:42 +02:00
TesselateBezierPatch ( dest , count , tess_u , tess_v , patch , origVertType ) ;
2013-09-22 09:51:32 +02:00
}
delete [ ] patches ;
2013-09-24 11:14:04 +02:00
u32 vertTypeWithIndex16 = ( vertType & ~ GE_VTYPE_IDX_MASK ) | GE_VTYPE_IDX_16BIT ;
2013-09-22 09:51:32 +02:00
2013-12-04 14:33:45 +01:00
UVScale prevUVScale ;
if ( g_Config . bPrescaleUV ) {
// We scaled during Normalize already so let's turn it off when drawing.
prevUVScale = gstate_c . uv ;
gstate_c . uv . uScale = 1.0f ;
gstate_c . uv . vScale = 1.0f ;
gstate_c . uv . uOff = 0 ;
gstate_c . uv . vOff = 0 ;
}
2013-11-14 14:03:03 +01:00
SubmitPrim ( decoded2 , quadIndices_ , GE_PRIM_TRIANGLES , count , vertTypeWithIndex16 , 0 ) ;
2013-08-30 10:25:01 +02:00
Flush ( ) ;
2013-12-04 14:33:45 +01:00
if ( g_Config . bPrescaleUV ) {
gstate_c . uv = prevUVScale ;
}
2013-08-30 10:25:01 +02:00
}