Network: Send IEEE floats (#7768)
This commit is contained in:
		
							parent
							
								
									8471d027b9
								
							
						
					
					
						commit
						839e935ba0
					
				| @ -1347,19 +1347,19 @@ void GenericCAO::processMessage(const std::string &data) | ||||
| 	} else if (cmd == GENERIC_CMD_UPDATE_POSITION) { | ||||
| 		// Not sent by the server if this object is an attachment.
 | ||||
| 		// We might however get here if the server notices the object being detached before the client.
 | ||||
| 		m_position = readV3F1000(is); | ||||
| 		m_velocity = readV3F1000(is); | ||||
| 		m_acceleration = readV3F1000(is); | ||||
| 		m_position = readV3F32(is); | ||||
| 		m_velocity = readV3F32(is); | ||||
| 		m_acceleration = readV3F32(is); | ||||
| 
 | ||||
| 		if (std::fabs(m_prop.automatic_rotate) < 0.001f) | ||||
| 			m_rotation = readV3F1000(is); | ||||
| 			m_rotation = readV3F32(is); | ||||
| 		else | ||||
| 			readV3F1000(is); | ||||
| 			readV3F32(is); | ||||
| 
 | ||||
| 		m_rotation = wrapDegrees_0_360_v3f(m_rotation); | ||||
| 		bool do_interpolate = readU8(is); | ||||
| 		bool is_end_position = readU8(is); | ||||
| 		float update_interval = readF1000(is); | ||||
| 		float update_interval = readF32(is); | ||||
| 
 | ||||
| 		// Place us a bit higher if we're physical, to not sink into
 | ||||
| 		// the ground due to sucky collision detection...
 | ||||
|  | ||||
| @ -49,19 +49,19 @@ std::string gob_cmd_update_position( | ||||
| 	// command
 | ||||
| 	writeU8(os, GENERIC_CMD_UPDATE_POSITION); | ||||
| 	// pos
 | ||||
| 	writeV3F1000(os, position); | ||||
| 	writeV3F32(os, position); | ||||
| 	// velocity
 | ||||
| 	writeV3F1000(os, velocity); | ||||
| 	writeV3F32(os, velocity); | ||||
| 	// acceleration
 | ||||
| 	writeV3F1000(os, acceleration); | ||||
| 	writeV3F32(os, acceleration); | ||||
| 	// rotation
 | ||||
| 	writeV3F1000(os, rotation); | ||||
| 	writeV3F32(os, rotation); | ||||
| 	// do_interpolate
 | ||||
| 	writeU8(os, do_interpolate); | ||||
| 	// is_end_position (for interpolation)
 | ||||
| 	writeU8(os, is_movement_end); | ||||
| 	// update_interval (for interpolation)
 | ||||
| 	writeF1000(os, update_interval); | ||||
| 	writeF32(os, update_interval); | ||||
| 	return os.str(); | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -191,6 +191,7 @@ with this program; if not, write to the Free Software Foundation, Inc., | ||||
| 	PROTOCOL VERSION 37: | ||||
| 		Redo detached inventory sending | ||||
| 		Add TOCLIENT_NODEMETA_CHANGED | ||||
| 		New network float format | ||||
| */ | ||||
| 
 | ||||
| #define LATEST_PROTOCOL_VERSION 37 | ||||
|  | ||||
| @ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., | ||||
| 
 | ||||
| #include "util/string.h" | ||||
| #include "util/serialize.h" | ||||
| #include <cmath> | ||||
| 
 | ||||
| class TestSerialization : public TestBase { | ||||
| public: | ||||
| @ -43,6 +44,7 @@ public: | ||||
| 	void testVecPut(); | ||||
| 	void testStringLengthLimits(); | ||||
| 	void testBufReader(); | ||||
| 	void testFloatFormat(); | ||||
| 
 | ||||
| 	std::string teststring2; | ||||
| 	std::wstring teststring2_w; | ||||
| @ -70,6 +72,7 @@ void TestSerialization::runTests(IGameDef *gamedef) | ||||
| 	TEST(testVecPut); | ||||
| 	TEST(testStringLengthLimits); | ||||
| 	TEST(testBufReader); | ||||
| 	TEST(testFloatFormat); | ||||
| } | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////
 | ||||
| @ -631,6 +634,75 @@ void TestSerialization::testBufReader() | ||||
| 	UASSERT(!buf.getRawDataNoEx(raw_data, sizeof(raw_data))); | ||||
| } | ||||
| 
 | ||||
| void TestSerialization::testFloatFormat() | ||||
| { | ||||
| 	FloatType type = getFloatSerializationType(); | ||||
| 	u32 i; | ||||
| 	f32 fs, fm; | ||||
| 
 | ||||
| 	// Check precision of float calculations on this platform
 | ||||
| 	const std::unordered_map<f32, u32> float_results = { | ||||
| 		{  0.0f, 0x00000000UL }, | ||||
| 		{  1.0f, 0x3F800000UL }, | ||||
| 		{ -1.0f, 0xBF800000UL }, | ||||
| 		{  0.1f, 0x3DCCCCCDUL }, | ||||
| 		{ -0.1f, 0xBDCCCCCDUL }, | ||||
| 		{ 1945329.25f, 0x49ED778AUL }, | ||||
| 		{ -23298764.f, 0xCBB1C166UL }, | ||||
| 		{  0.5f, 0x3F000000UL }, | ||||
| 		{ -0.5f, 0xBF000000UL } | ||||
| 	}; | ||||
| 	for (const auto &v : float_results) { | ||||
| 		i = f32Tou32Slow(v.first); | ||||
| 		if (std::abs((s64)v.second - i) > 32) { | ||||
| 			printf("Inaccurate float values on %.9g, expected 0x%X, actual 0x%X\n", | ||||
| 				v.first, v.second, i); | ||||
| 			UASSERT(false); | ||||
| 		} | ||||
| 
 | ||||
| 		fs = u32Tof32Slow(v.second); | ||||
| 		if (std::fabs(v.first - fs) > std::fabs(v.first * 0.000005f)) { | ||||
| 			printf("Inaccurate float values on 0x%X, expected %.9g, actual 0x%.9g\n", | ||||
| 				v.second, v.first, fs); | ||||
| 			UASSERT(false); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (type == FLOATTYPE_SLOW) { | ||||
| 		// conversion using memcpy is not possible
 | ||||
| 		// Skip exact float comparison checks below
 | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	auto test_single = [&fs, &fm](const u32 &i) -> bool { | ||||
| 		memcpy(&fm, &i, 4); | ||||
| 		fs = u32Tof32Slow(i); | ||||
| 		if (fm != fs) { | ||||
| 			printf("u32Tof32Slow failed on 0x%X, expected %.9g, actual %.9g\n", | ||||
| 				i, fm, fs); | ||||
| 			return false; | ||||
| 		} | ||||
| 		if (f32Tou32Slow(fs) != i) { | ||||
| 			printf("f32Tou32Slow failed on %.9g, expected 0x%X, actual 0x%X\n", | ||||
| 				fs, i, f32Tou32Slow(fs)); | ||||
| 			return false; | ||||
| 		} | ||||
| 		return true; | ||||
| 	}; | ||||
| 
 | ||||
| 	// Use step of prime 277 to speed things up from 3 minutes to a few seconds
 | ||||
| 	// Test from 0 to 0xFF800000UL (positive)
 | ||||
| 	for (i = 0x00000000UL; i <= 0x7F800000UL; i += 277) | ||||
| 		UASSERT(test_single(i)); | ||||
| 
 | ||||
| 	// Ensure +inf and -inf are tested
 | ||||
| 	UASSERT(test_single(0x7F800000UL)); | ||||
| 	UASSERT(test_single(0xFF800000UL)); | ||||
| 
 | ||||
| 	// Test from 0x80000000UL to 0xFF800000UL (negative)
 | ||||
| 	for (i = 0x80000000UL; i <= 0xFF800000UL; i += 277) | ||||
| 		UASSERT(test_single(i)); | ||||
| } | ||||
| 
 | ||||
| const u8 TestSerialization::test_serialized_data[12 * 13] = { | ||||
| 	0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, | ||||
|  | ||||
| @ -4,6 +4,7 @@ set(UTIL_SRCS | ||||
| 	${CMAKE_CURRENT_SOURCE_DIR}/base64.cpp | ||||
| 	${CMAKE_CURRENT_SOURCE_DIR}/directiontables.cpp | ||||
| 	${CMAKE_CURRENT_SOURCE_DIR}/enriched_string.cpp | ||||
| 	${CMAKE_CURRENT_SOURCE_DIR}/ieee_float.cpp | ||||
| 	${CMAKE_CURRENT_SOURCE_DIR}/numeric.cpp | ||||
| 	${CMAKE_CURRENT_SOURCE_DIR}/pointedthing.cpp | ||||
| 	${CMAKE_CURRENT_SOURCE_DIR}/serialize.cpp | ||||
|  | ||||
							
								
								
									
										136
									
								
								src/util/ieee_float.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								src/util/ieee_float.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,136 @@ | ||||
| /*
 | ||||
|  * Conversion of f32 to IEEE-754 and vice versa. | ||||
|  * | ||||
|  * © Copyright 2018 Pedro Gimeno Fortea. | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining | ||||
|  * a copy of this software and associated documentation files (the | ||||
|  * "Software"), to deal in the Software without restriction, including | ||||
|  * without limitation the rights to use, copy, modify, merge, publish, | ||||
|  * distribute, sublicense, and/or sell copies of the Software, and to | ||||
|  * permit persons to whom the Software is furnished to do so, subject to | ||||
|  * the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included | ||||
|  * in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||||
|  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||||
|  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | ||||
|  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | ||||
|  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | ||||
|  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| #include "ieee_float.h" | ||||
| #include "log.h" | ||||
| #include "porting.h" | ||||
| #include <limits> | ||||
| #include <cmath> | ||||
| 
 | ||||
| // Given an unsigned 32-bit integer representing an IEEE-754 single-precision
 | ||||
| // float, return the float.
 | ||||
| f32 u32Tof32Slow(u32 i) | ||||
| { | ||||
| 	// clang-format off
 | ||||
| 	int exp = (i >> 23) & 0xFF; | ||||
| 	u32 sign = i & 0x80000000UL; | ||||
| 	u32 imant = i & 0x7FFFFFUL; | ||||
| 	if (exp == 0xFF) { | ||||
| 		// Inf/NaN
 | ||||
| 		if (imant == 0) { | ||||
| 			if (std::numeric_limits<f32>::has_infinity)	 | ||||
| 				return sign ? -std::numeric_limits<f32>::infinity() : | ||||
| 					std::numeric_limits<f32>::infinity(); | ||||
| 			return sign ? std::numeric_limits<f32>::max() : | ||||
| 				std::numeric_limits<f32>::lowest(); | ||||
| 		} | ||||
| 		return std::numeric_limits<f32>::has_quiet_NaN ? | ||||
| 			std::numeric_limits<f32>::quiet_NaN() : -0.f; | ||||
| 	} | ||||
| 
 | ||||
| 	if (!exp) { | ||||
| 		// Denormal or zero
 | ||||
| 		return sign ? -ldexpf((f32)imant, -149) : ldexpf((f32)imant, -149); | ||||
| 	} | ||||
| 
 | ||||
| 	return sign ? -ldexpf((f32)(imant | 0x800000UL), exp - 150) : | ||||
| 		ldexpf((f32)(imant | 0x800000UL), exp - 150); | ||||
| 	// clang-format on
 | ||||
| } | ||||
| 
 | ||||
| // Given a float, return an unsigned 32-bit integer representing the f32
 | ||||
| // in IEEE-754 single-precision format.
 | ||||
| u32 f32Tou32Slow(f32 f) | ||||
| { | ||||
| 	u32 signbit = std::copysign(1.0f, f) == 1.0f ? 0 : 0x80000000UL; | ||||
| 	if (f == 0.f) | ||||
| 		return signbit; | ||||
| 	if (std::isnan(f)) | ||||
| 		return signbit | 0x7FC00000UL; | ||||
| 	if (std::isinf(f)) | ||||
| 		return signbit | 0x7F800000UL; | ||||
| 	int exp; | ||||
| 	f32 mant = frexpf(f, &exp); | ||||
| 	u32 imant = (u32)std::floor((signbit ? -16777216.f : 16777216.f) * mant); | ||||
| 	exp += 126; | ||||
| 	if (exp <= 0) { | ||||
| 		// Denormal
 | ||||
| 		return signbit | (exp <= -31 ? 0 : imant >> (1 - exp)); | ||||
| 	} | ||||
| 
 | ||||
| 	if (exp >= 255) { | ||||
| 		// Overflow due to the platform having exponents bigger than IEEE ones.
 | ||||
| 		// Return signed infinity.
 | ||||
| 		return signbit | 0x7F800000UL; | ||||
| 	} | ||||
| 
 | ||||
| 	// Regular number
 | ||||
| 	return signbit | (exp << 23) | (imant & 0x7FFFFFUL); | ||||
| } | ||||
| 
 | ||||
| // This test needs the following requisites in order to work:
 | ||||
| // - The float type must be a 32 bits IEEE-754 single-precision float.
 | ||||
| // - The endianness of f32s and integers must match.
 | ||||
| FloatType getFloatSerializationType() | ||||
| { | ||||
| 	// clang-format off
 | ||||
| 	const f32 cf = -22220490.f; | ||||
| 	const u32 cu = 0xCBA98765UL; | ||||
| 	if (std::numeric_limits<f32>::is_iec559 && sizeof(cf) == 4 && | ||||
| 			sizeof(cu) == 4 && !memcmp(&cf, &cu, 4)) { | ||||
| 		// u32Tof32Slow and f32Tou32Slow are not needed, use memcpy
 | ||||
| 		return FLOATTYPE_SYSTEM; | ||||
| 	} | ||||
| 
 | ||||
| 	// Run quick tests to ensure the custom functions provide acceptable results
 | ||||
| 	warningstream << "floatSerialization: f32 and u32 endianness are " | ||||
| 		"not equal or machine is not IEEE-754 compliant" << std::endl; | ||||
| 	u32 i; | ||||
| 	char buf[128]; | ||||
| 
 | ||||
| 	// NaN checks aren't included in the main loop
 | ||||
| 	if (!std::isnan(u32Tof32Slow(0x7FC00000UL))) { | ||||
| 		porting::mt_snprintf(buf, sizeof(buf), | ||||
| 			"u32Tof32Slow(0x7FC00000) failed to produce a NaN, actual: %.9g", | ||||
| 			u32Tof32Slow(0x7FC00000UL)); | ||||
| 		infostream << buf << std::endl; | ||||
| 	} | ||||
| 	if (!std::isnan(u32Tof32Slow(0xFFC00000UL))) { | ||||
| 		porting::mt_snprintf(buf, sizeof(buf), | ||||
| 			"u32Tof32Slow(0xFFC00000) failed to produce a NaN, actual: %.9g", | ||||
| 			u32Tof32Slow(0xFFC00000UL)); | ||||
| 		infostream << buf << std::endl; | ||||
| 	} | ||||
| 
 | ||||
| 	i = f32Tou32Slow(std::numeric_limits<f32>::quiet_NaN()); | ||||
| 	// check that it corresponds to a NaN encoding
 | ||||
| 	if ((i & 0x7F800000UL) != 0x7F800000UL || (i & 0x7FFFFFUL) == 0) { | ||||
| 		porting::mt_snprintf(buf, sizeof(buf), | ||||
| 			"f32Tou32Slow(NaN) failed to encode NaN, actual: 0x%X", i); | ||||
| 		infostream << buf << std::endl; | ||||
| 	} | ||||
| 
 | ||||
| 	return FLOATTYPE_SLOW; | ||||
| 	// clang-format on
 | ||||
| } | ||||
							
								
								
									
										34
									
								
								src/util/ieee_float.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/util/ieee_float.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,34 @@ | ||||
| /*
 | ||||
| Minetest | ||||
| Copyright (C) 2018 SmallJoker <mk939@ymail.com> | ||||
| 
 | ||||
| This program is free software; you can redistribute it and/or modify | ||||
| it under the terms of the GNU Lesser General Public License as published by | ||||
| the Free Software Foundation; either version 2.1 of the License, or | ||||
| (at your option) any later version. | ||||
| 
 | ||||
| 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 Lesser General Public License for more details. | ||||
| 
 | ||||
| You should have received a copy of the GNU Lesser General Public License along | ||||
| with this program; if not, write to the Free Software Foundation, Inc., | ||||
| 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| */ | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "irrlichttypes.h" | ||||
| 
 | ||||
| enum FloatType | ||||
| { | ||||
| 	FLOATTYPE_UNKNOWN, | ||||
| 	FLOATTYPE_SLOW, | ||||
| 	FLOATTYPE_SYSTEM | ||||
| }; | ||||
| 
 | ||||
| f32 u32Tof32Slow(u32 i); | ||||
| u32 f32Tou32Slow(f32 f); | ||||
| 
 | ||||
| FloatType getFloatSerializationType(); | ||||
| @ -28,6 +28,8 @@ with this program; if not, write to the Free Software Foundation, Inc., | ||||
| #include <iomanip> | ||||
| #include <vector> | ||||
| 
 | ||||
| FloatType g_serialize_f32_type = FLOATTYPE_UNKNOWN; | ||||
| 
 | ||||
| ////
 | ||||
| //// BufReader
 | ||||
| ////
 | ||||
|  | ||||
| @ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., | ||||
| #include "irrlichttypes_bloated.h" | ||||
| #include "exceptions.h" // for SerializationError | ||||
| #include "debug.h" // for assert | ||||
| #include "ieee_float.h" | ||||
| 
 | ||||
| #include "config.h" | ||||
| #if HAVE_ENDIAN_H | ||||
| @ -60,6 +61,8 @@ with this program; if not, write to the Free Software Foundation, Inc., | ||||
| #define LONG_STRING_MAX_LEN (64 * 1024 * 1024) | ||||
| 
 | ||||
| 
 | ||||
| extern FloatType g_serialize_f32_type; | ||||
| 
 | ||||
| #if HAVE_ENDIAN_H | ||||
| // use machine native byte swapping routines
 | ||||
| // Note: memcpy below is optimized out by modern compilers
 | ||||
| @ -188,6 +191,25 @@ inline f32 readF1000(const u8 *data) | ||||
| 	return (f32)readS32(data) / FIXEDPOINT_FACTOR; | ||||
| } | ||||
| 
 | ||||
| inline f32 readF32(const u8 *data) | ||||
| { | ||||
| 	u32 u = readU32(data); | ||||
| 
 | ||||
| 	switch (g_serialize_f32_type) { | ||||
| 	case FLOATTYPE_SYSTEM: { | ||||
| 			f32 f; | ||||
| 			memcpy(&f, &u, 4); | ||||
| 			return f; | ||||
| 		} | ||||
| 	case FLOATTYPE_SLOW: | ||||
| 		return u32Tof32Slow(u); | ||||
| 	case FLOATTYPE_UNKNOWN: // First initialization
 | ||||
| 		g_serialize_f32_type = getFloatSerializationType(); | ||||
| 		return readF32(data); | ||||
| 	} | ||||
| 	throw SerializationError("readF32: Unreachable code"); | ||||
| } | ||||
| 
 | ||||
| inline video::SColor readARGB8(const u8 *data) | ||||
| { | ||||
| 	video::SColor p(readU32(data)); | ||||
| @ -245,6 +267,15 @@ inline v3f readV3F1000(const u8 *data) | ||||
| 	return p; | ||||
| } | ||||
| 
 | ||||
| inline v3f readV3F32(const u8 *data) | ||||
| { | ||||
| 	v3f p; | ||||
| 	p.X = (float)readF32(&data[0]); | ||||
| 	p.Y = (float)readF32(&data[4]); | ||||
| 	p.Z = (float)readF32(&data[8]); | ||||
| 	return p; | ||||
| } | ||||
| 
 | ||||
| /////////////// write routines ////////////////
 | ||||
| 
 | ||||
| inline void writeU8(u8 *data, u8 i) | ||||
| @ -259,7 +290,7 @@ inline void writeS8(u8 *data, s8 i) | ||||
| 
 | ||||
| inline void writeS16(u8 *data, s16 i) | ||||
| { | ||||
| 	writeU16(data, (u16)i); | ||||
| 	writeU16(data, (u16)i);  | ||||
| } | ||||
| 
 | ||||
| inline void writeS32(u8 *data, s32 i) | ||||
| @ -278,6 +309,23 @@ inline void writeF1000(u8 *data, f32 i) | ||||
| 	writeS32(data, i * FIXEDPOINT_FACTOR); | ||||
| } | ||||
| 
 | ||||
| inline void writeF32(u8 *data, f32 i) | ||||
| { | ||||
| 	switch (g_serialize_f32_type) { | ||||
| 	case FLOATTYPE_SYSTEM: { | ||||
| 			u32 u; | ||||
| 			memcpy(&u, &i, 4); | ||||
| 			return writeU32(data, u); | ||||
| 		} | ||||
| 	case FLOATTYPE_SLOW: | ||||
| 		return writeU32(data, f32Tou32Slow(i)); | ||||
| 	case FLOATTYPE_UNKNOWN: // First initialization
 | ||||
| 		g_serialize_f32_type = getFloatSerializationType(); | ||||
| 		return writeF32(data, i); | ||||
| 	} | ||||
| 	throw SerializationError("writeF32: Unreachable code"); | ||||
| } | ||||
| 
 | ||||
| inline void writeARGB8(u8 *data, video::SColor p) | ||||
| { | ||||
| 	writeU32(data, p.color); | ||||
| @ -322,6 +370,13 @@ inline void writeV3F1000(u8 *data, v3f p) | ||||
| 	writeF1000(&data[8], p.Z); | ||||
| } | ||||
| 
 | ||||
| inline void writeV3F32(u8 *data, v3f p) | ||||
| { | ||||
| 	writeF32(&data[0], p.X); | ||||
| 	writeF32(&data[4], p.Y); | ||||
| 	writeF32(&data[8], p.Z); | ||||
| } | ||||
| 
 | ||||
| ////
 | ||||
| //// Iostream wrapper for data read/write
 | ||||
| ////
 | ||||
| @ -351,12 +406,14 @@ MAKE_STREAM_READ_FXN(s16,   S16,      2); | ||||
| MAKE_STREAM_READ_FXN(s32,   S32,      4); | ||||
| MAKE_STREAM_READ_FXN(s64,   S64,      8); | ||||
| MAKE_STREAM_READ_FXN(f32,   F1000,    4); | ||||
| MAKE_STREAM_READ_FXN(f32,   F32,      4); | ||||
| MAKE_STREAM_READ_FXN(v2s16, V2S16,    4); | ||||
| MAKE_STREAM_READ_FXN(v3s16, V3S16,    6); | ||||
| MAKE_STREAM_READ_FXN(v2s32, V2S32,    8); | ||||
| MAKE_STREAM_READ_FXN(v3s32, V3S32,   12); | ||||
| MAKE_STREAM_READ_FXN(v2f,   V2F1000,  8); | ||||
| MAKE_STREAM_READ_FXN(v3f,   V3F1000, 12); | ||||
| MAKE_STREAM_READ_FXN(v3f,   V3F32,   12); | ||||
| MAKE_STREAM_READ_FXN(video::SColor, ARGB8, 4); | ||||
| 
 | ||||
| MAKE_STREAM_WRITE_FXN(u8,    U8,       1); | ||||
| @ -368,12 +425,14 @@ MAKE_STREAM_WRITE_FXN(s16,   S16,      2); | ||||
| MAKE_STREAM_WRITE_FXN(s32,   S32,      4); | ||||
| MAKE_STREAM_WRITE_FXN(s64,   S64,      8); | ||||
| MAKE_STREAM_WRITE_FXN(f32,   F1000,    4); | ||||
| MAKE_STREAM_WRITE_FXN(f32,   F32,      4); | ||||
| MAKE_STREAM_WRITE_FXN(v2s16, V2S16,    4); | ||||
| MAKE_STREAM_WRITE_FXN(v3s16, V3S16,    6); | ||||
| MAKE_STREAM_WRITE_FXN(v2s32, V2S32,    8); | ||||
| MAKE_STREAM_WRITE_FXN(v3s32, V3S32,   12); | ||||
| MAKE_STREAM_WRITE_FXN(v2f,   V2F1000,  8); | ||||
| MAKE_STREAM_WRITE_FXN(v3f,   V3F1000, 12); | ||||
| MAKE_STREAM_WRITE_FXN(v3f,   V3F32,   12); | ||||
| MAKE_STREAM_WRITE_FXN(video::SColor, ARGB8, 4); | ||||
| 
 | ||||
| ////
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user