// Copyright (C) 2012-2015 ChaosForge Ltd // http://chaosforge.org/ // // This file is part of Nova libraries. // For conditions of distribution and use, see copying.txt file in root folder. #include "nv/lua/lua_math.hh" #include "nv/lua/lua_raw.hh" #include "nv/lua/lua_aux.hh" #include "nv/stl/type_traits/common.hh" static size_t nlua_swizzel_lookup[256]; using nv::lua::detail::is_vec; using nv::lua::detail::to_vec; using nv::lua::detail::to_pvec; using nv::lua::detail::push_vec; inline bool nlua_is_swizzel( const unsigned char* str, size_t max ) { while (*str) { if (nlua_swizzel_lookup[*str] > max) return false; str++; } return true; } template < typename T, size_t k > struct nlua_vec_constructor { static inline T unit() { return T(); } static inline T construct( lua_State*, int ) { return T(); } }; template < typename T > struct nlua_vec_constructor< T, 1 > { static inline T unit() { return T( 1 ); } static inline T construct( lua_State* L, int index ) { return T( lua_tonumber( L, index ) ); } }; template < typename T > struct nlua_vec_constructor< T, 2 > { static inline T unit() { return T( 1, 1 ); } static inline T construct( lua_State* L, int index ) { if ( lua_type( L, index ) == LUA_TUSERDATA ) return to_vec( L, index ); else return T( lua_tonumber( L, index ), lua_tonumber( L, index + 1 ) ); } }; template < typename T > struct nlua_vec_constructor< T, 3 > { static inline T unit() { return T( 1, 1, 1 ); } static inline T construct( lua_State* L, int index ) { typedef nv::math::tvec2 vec2; if ( lua_type( L, index ) == LUA_TUSERDATA ) { if ( is_vec( L, index ) ) return to_vec( L, index ); else return T( to_vec( L, index ), lua_tonumber( L, index + 1 ) ); } else { if ( lua_type( L, index+1 ) == LUA_TUSERDATA ) return T( lua_tonumber( L, index ), to_vec( L, index+1 ) ); else return T( lua_tonumber( L, index ), lua_tonumber( L, index + 1 ), lua_tonumber( L, index + 2 ) ); } } }; template < typename T > struct nlua_vec_constructor< T, 4 > { static inline T unit() { return T( 1, 1, 1, 1 ); } static inline T construct( lua_State* L, int index ) { typedef nv::math::tvec2 vec2; typedef nv::math::tvec3 vec3; if ( lua_type( L, index ) == LUA_TUSERDATA ) { if ( is_vec( L, index ) ) return to_vec( L, index ); else { if ( is_vec( L, index ) ) return T( to_vec( L, index ), lua_tonumber( L, index + 1 ) ); else { if ( lua_type( L, index+1 ) == LUA_TUSERDATA ) return T( to_vec( L, index ), to_vec( L, index + 1 ) ); else return T( to_vec( L, index ), lua_tonumber( L, index + 1 ), lua_tonumber( L, index + 2 ) ); } } } else { if ( lua_type( L, index+1 ) == LUA_TUSERDATA ) { if ( is_vec( L, index+1 ) ) return T( lua_tonumber( L, index ), to_vec( L, index+1 ) ); else return T( lua_tonumber( L, index ), to_vec( L, index+1 ), lua_tonumber( L, index + 2 ) ); } else { if ( lua_type( L, index+2 ) == LUA_TUSERDATA ) return T( lua_tonumber( L, index ), lua_tonumber( L, index + 1 ), to_vec( L, index+2 ) ); else return T( lua_tonumber( L, index ), lua_tonumber( L, index + 1 ), lua_tonumber( L, index + 2 ), lua_tonumber( L, index + 3 ) ); } } } }; template< typename T > int nlua_vec_new( lua_State* L ) { push_vec( L, nlua_vec_constructor::construct( L, 1 ) ); return 1; } template< typename T > int nlua_vec_random( lua_State* L ) { push_vec( L, nv::lua::rng().range( to_vec( L, 1 ), to_vec( L, 2 ) ) ); return 1; } template< typename T > int nlua_vec_clone( lua_State* L ) { push_vec( L, to_vec( L, 1 ) ); return 1; } template< typename T > int nlua_vec_sign( lua_State* L ) { push_vec( L, nv::math::sign( to_vec( L, 1 ) ) ); return 1; } template< typename T > int nlua_vec_call( lua_State* L ) { push_vec( L, nlua_vec_constructor::construct( L, 2 ) ); return 1; } template< typename T > static int nlua_vec_unm( lua_State* L ) { push_vec( L, -to_vec( L, 1 ) ); return 1; } template< typename T > int nlua_vec_add( lua_State* L ) { T v; if ( lua_type( L, 1 ) == LUA_TNUMBER ) v = static_cast(lua_tonumber( L, 1 )) + to_vec( L, 2 ); else if ( lua_type( L, 2 ) == LUA_TNUMBER ) v = to_vec( L, 1 ) + static_cast( lua_tonumber( L, 2 ) ); else v = to_vec( L, 1 ) + to_vec( L, 2 ); push_vec( L, v ); return 1; } template< typename T > int nlua_vec_sub( lua_State* L ) { T v; if ( lua_type( L, 1 ) == LUA_TNUMBER ) v = static_cast(lua_tonumber( L, 1 )) - to_vec( L, 2 ); else if ( lua_type( L, 2 ) == LUA_TNUMBER ) v = to_vec( L, 1 ) - static_cast(lua_tonumber( L, 2 ) ); else v = to_vec( L, 1 ) - to_vec( L, 2 ); push_vec( L, v ); return 1; } template< typename T > int nlua_vec_mul( lua_State* L ) { T v; if ( lua_type( L, 1 ) == LUA_TNUMBER ) v = static_cast(lua_tonumber( L, 1 )) * to_vec( L, 2 ); else if ( lua_type( L, 2 ) == LUA_TNUMBER ) v = to_vec( L, 1 ) * static_cast(lua_tonumber( L, 2 )); else v = to_vec( L, 1 ) * to_vec( L, 2 ); push_vec( L, v ); return 1; } template< typename T > int nlua_vec_div( lua_State* L ) { T v; if ( lua_type( L, 1 ) == LUA_TNUMBER ) v = static_cast(lua_tonumber( L, 1 )) / to_vec( L, 2 ); else if ( lua_type( L, 2 ) == LUA_TNUMBER ) v = to_vec( L, 1 ) / static_cast(lua_tonumber( L, 2 )); else v = to_vec( L, 1 ) / to_vec( L, 2 ); push_vec( L, v ); return 1; } template< typename T > int nlua_vec_eq( lua_State* L ) { lua_pushboolean( L, to_vec( L, 1 ) == to_vec( L, 2 ) ); return 1; } template< typename T > int nlua_vec_get( lua_State* L ) { T v = to_vec( L, 1 ); for ( size_t i = 0; i < v.size(); ++i ) { lua_pushnumber( L, v[i] ); } return v.size(); } template< typename T > int nlua_vec_index( lua_State* L ) { T* v = to_pvec( L, 1 ); size_t len = 0; size_t vlen = v->size(); const unsigned char * key = reinterpret_cast( lua_tolstring( L, 2, &len ) ); size_t idx = 255; if ( len == 1 ) { idx = nlua_swizzel_lookup[ key[ 0 ] ]; if ( idx < vlen ) { lua_pushnumber( L, (*v)[idx] ); return 1; } } else if ( len < 4 && nlua_is_swizzel(key,vlen-1) ) { switch (len) { case 2 : push_vec( L, nv::math::tvec2( (*v)[nlua_swizzel_lookup[key[0]]], (*v)[nlua_swizzel_lookup[key[1]]] ) ); return 1; case 3 : push_vec( L, nv::math::tvec3( (*v)[nlua_swizzel_lookup[key[0]]], (*v)[nlua_swizzel_lookup[key[1]]], (*v)[nlua_swizzel_lookup[key[2]]] ) ); return 1; case 4 : push_vec( L, nv::math::tvec4( (*v)[nlua_swizzel_lookup[key[0]]], (*v)[nlua_swizzel_lookup[key[1]]], (*v)[nlua_swizzel_lookup[key[2]]], (*v)[nlua_swizzel_lookup[key[3]]] ) ); return 1; default: break; } } luaL_getmetafield( L, 1, "__functions" ); lua_pushvalue( L, 2 ); lua_rawget( L, -2 ); return 1; } template< typename T > int nlua_vec_newindex( lua_State* L ) { typedef nv::math::tvec2 vec2; typedef nv::math::tvec3 vec3; typedef nv::math::tvec4 vec4; T* v = to_pvec( L, 1 ); size_t len = 0; size_t vlen = v->size(); const unsigned char * key = reinterpret_cast( lua_tolstring( L, 2, &len ) ); size_t idx = 255; if( len == 1 ) { idx = nlua_swizzel_lookup[ key[ 0 ] ]; if ( idx < vlen ) { (*v)[idx] = static_cast( luaL_checknumber( L, 3 ) ); return 0; } } else if ( len < 4 && nlua_is_swizzel(key,vlen-1) ) { switch (len) { case 2 : { vec2 v2 = to_vec(L,3); for ( size_t i = 0; i< len; ++i) (*v)[nlua_swizzel_lookup[key[i]]] = v2[i]; } return 0; case 3 : { vec3 v3 = to_vec(L,3); for ( size_t i = 0; i< len; ++i) (*v)[nlua_swizzel_lookup[key[i]]] = v3[i]; } return 0; case 4 : { vec4 v4 = to_vec(L,3); for ( size_t i = 0; i< len; ++i) (*v)[nlua_swizzel_lookup[key[i]]] = v4[i]; } return 0; default: break; } } return 0; } template< typename T > static int nlua_vec_tostring( lua_State* L ) { T v = to_vec( L, 1 ); bool fl = nv::is_floating_point::value; switch ( v.size() ) { case 1: lua_pushfstring( L, ( fl ? "(%f)" : "(%d)" ), v[0] ); break; case 2: lua_pushfstring( L, ( fl ? "(%f,%f)" : "(%d,%d)" ), v[0], v[1] ); break; case 3: lua_pushfstring( L, ( fl ? "(%f,%f,%f)" : "(%d,%d,%d)" ), v[0], v[1], v[2] ); break; case 4: lua_pushfstring( L, ( fl ? "(%f,%f,%f,%f)" : "(%d,%d,%d,%d)" ), v[0], v[1], v[2], v[3] ); break; default: lua_pushliteral( L, "(vector?)" ); break; } return 1; } template< typename T > int luaopen_vec( lua_State * L ) { static const struct luaL_Reg nlua_vec_sf [] = { { "new", nlua_vec_new }, { "random", nlua_vec_random }, {NULL, NULL} }; static const struct luaL_Reg nlua_vec_f [] = { { "clone", nlua_vec_clone }, { "sign", nlua_vec_sign }, { "get", nlua_vec_get }, { "tostring", nlua_vec_tostring }, {NULL, NULL} }; static const struct luaL_Reg nlua_vec_sm [] = { { "__call", nlua_vec_call }, {NULL, NULL} }; static const struct luaL_Reg nlua_vec_m [] = { { "__add", nlua_vec_add }, { "__sub", nlua_vec_sub }, { "__unm", nlua_vec_unm }, { "__mul", nlua_vec_mul }, { "__div", nlua_vec_div }, { "__eq", nlua_vec_eq }, { "__index", nlua_vec_index }, { "__newindex", nlua_vec_newindex }, { "__tostring", nlua_vec_tostring }, {NULL, NULL} }; luaL_newmetatable( L, nv::lua::pass_traits::metatable() ); nlua_register( L, nlua_vec_m, -1 ); lua_createtable( L, 0, 0 ); nlua_register( L, nlua_vec_f, -1 ); lua_setfield(L, -2, "__functions" ); lua_pop( L, 1 ); lua_createtable( L, 0, 0 ); nlua_register( L, nlua_vec_sf, -1 ); lua_createtable( L, 0, 0 ); nlua_register( L, nlua_vec_sm, -1 ); lua_setmetatable( L, -2 ); nv::lua::detail::push_vec( L, T() ); lua_setfield( L, -2, "ZERO" ); nv::lua::detail::push_vec( L, nlua_vec_constructor::unit() ); lua_setfield( L, -2, "UNIT" ); return 1; } template < typename T > void nlua_rtti_vec_push( nv::lua::state* state, const nv::type_entry*, void* object ) { T* value = reinterpret_cast( object ); push_vec( state->get_raw(), *value ); } template < typename T > bool nlua_rtti_vec_read( nv::lua::state* state, const nv::type_entry*, void* object, int index ) { T* value = reinterpret_cast( object ); int type = lua_type( state->get_raw(), index ); if ( type == LUA_TUSERDATA ) { T* from = to_pvec( state->get_raw(), index ); if ( !from ) return false; *value = *from; } // else if ( type == LUA_TTABLE ) // { // // } else return false; int todo; int table_constructor; return true; } void nv::lua::register_math( lua::state* state ) { for (size_t i = 0; i < 256; ++i ) nlua_swizzel_lookup[i] = 255; using nv::uchar8; nlua_swizzel_lookup[uchar8( 'x' )] = 0; nlua_swizzel_lookup[uchar8( 'r' )] = 0; nlua_swizzel_lookup[uchar8( 's' )] = 0; nlua_swizzel_lookup[uchar8( '0' )] = 0; nlua_swizzel_lookup[uchar8( 'y' )] = 1; nlua_swizzel_lookup[uchar8( 'g' )] = 1; nlua_swizzel_lookup[uchar8( 't' )] = 0; nlua_swizzel_lookup[uchar8( '1' )] = 1; nlua_swizzel_lookup[uchar8( 'z' )] = 2; nlua_swizzel_lookup[uchar8( 'b' )] = 2; nlua_swizzel_lookup[uchar8( 'u' )] = 0; nlua_swizzel_lookup[uchar8( '2' )] = 2; nlua_swizzel_lookup[uchar8( 'w' )] = 3; nlua_swizzel_lookup[uchar8( 'a' )] = 3; nlua_swizzel_lookup[uchar8( 'v' )] = 0; nlua_swizzel_lookup[uchar8( '3' )] = 3; lua_State* L = state->get_raw(); int stack = lua_gettop( L ); nlua_requiref(L, "coord", luaopen_vec, 1); nlua_requiref(L, "ivec2", luaopen_vec, 1); nlua_requiref(L, "ivec3", luaopen_vec, 1); nlua_requiref(L, "ivec4", luaopen_vec, 1); nlua_requiref(L, "vec2", luaopen_vec, 1); nlua_requiref(L, "vec3", luaopen_vec, 1); nlua_requiref(L, "vec4", luaopen_vec, 1); lua_settop( L, stack ); state->register_rtti_type< nv::ivec2 >( nlua_rtti_vec_push, nlua_rtti_vec_read ); state->register_rtti_type< nv::ivec3 >( nlua_rtti_vec_push, nlua_rtti_vec_read ); state->register_rtti_type< nv::ivec4 >( nlua_rtti_vec_push, nlua_rtti_vec_read ); state->register_rtti_type< nv::vec2 > ( nlua_rtti_vec_push, nlua_rtti_vec_read ); state->register_rtti_type< nv::vec3 > ( nlua_rtti_vec_push, nlua_rtti_vec_read ); state->register_rtti_type< nv::vec4 > ( nlua_rtti_vec_push, nlua_rtti_vec_read ); }