commit x64 compilation from lulu cause the other branch dont seems to compile properly at home
This commit is contained in:
228
include/framework/ascii/ascii_encoder.cpp
Normal file
228
include/framework/ascii/ascii_encoder.cpp
Normal file
@ -0,0 +1,228 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "ascii/ascii_encoder.h"
|
||||
#include "log/log.h"
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
#define FEED_OUT(out_c) \
|
||||
{ \
|
||||
if (out) \
|
||||
{ \
|
||||
if (olen < max) \
|
||||
out[olen] = (uchar)(out_c); \
|
||||
else \
|
||||
break; \
|
||||
} \
|
||||
olen++; \
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
#define FEED_IN(in_v) \
|
||||
{ \
|
||||
if (!len) \
|
||||
{ \
|
||||
__LOG_W__ << "input buffer underflow.\n"; \
|
||||
break; \
|
||||
} \
|
||||
(in_v) = (int)*in++; \
|
||||
len--; \
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
|
||||
From UUencode wikipedia.
|
||||
------------------------
|
||||
|
||||
(...)
|
||||
Uuencode repeatedly takes in a group of three bytes, adding trailing zeros
|
||||
if there are less than three bytes left. These 24 bits are split into four
|
||||
groups of six which are treated as numbers between 0 and 63.
|
||||
Decimal 32 is added to each number and they are ouput as ASCII characters
|
||||
which will lie in the range 32 (space) to 32+63 = 95 (underscore).
|
||||
ASCII characters greater than 95 may also be used; however, only the six
|
||||
right-most bits are relevant.
|
||||
Each group of sixty output characters (corresponding to 45 input bytes) is
|
||||
output as a separate line preceded by an 'M' (ASCII code 77 = 32+45).
|
||||
At the end of the input, if there are N output characters left after the
|
||||
last group of sixty and N>0 then they will be preceded by the character
|
||||
whose code is 32+N.
|
||||
(...)
|
||||
|
||||
*/
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
uint nAsciiEncoder::UUEncode(const uchar *in, size_t len, uchar *out, size_t max)
|
||||
{
|
||||
uint olen = 0, n;
|
||||
|
||||
while (len)
|
||||
{
|
||||
uchar *p = out ? &out[olen] : NULL;
|
||||
FEED_OUT(0) // Dummy feed.
|
||||
|
||||
for (n = 0; (n < 15) && len; n++)
|
||||
{
|
||||
uchar a, b, c;
|
||||
a = *in++;
|
||||
len--;
|
||||
if (len) { len--; b = *in++; } else b = 0;
|
||||
if (len) { len--; c = *in++; } else c = 0;
|
||||
|
||||
uint f = (a << 16) + (b << 8) + c;
|
||||
uchar w, x, y, z;
|
||||
|
||||
z = (f & 63) + 32;
|
||||
y = ((f >> 6) & 63) + 32;
|
||||
x = ((f >> 12) & 63) + 32;
|
||||
w = ((f >> 18) & 63) + 32;
|
||||
|
||||
FEED_OUT(w);
|
||||
FEED_OUT(x);
|
||||
FEED_OUT(y);
|
||||
FEED_OUT(z);
|
||||
}
|
||||
if (p)
|
||||
p[0] = (uchar)(n * 3 + 32);
|
||||
FEED_OUT('\n')
|
||||
}
|
||||
return olen;
|
||||
}
|
||||
uint nAsciiEncoder::UUDecode(const uchar *in, size_t len, uchar *out, size_t max)
|
||||
{
|
||||
uint olen = 0, n;
|
||||
|
||||
while (len)
|
||||
{
|
||||
uint lsize;
|
||||
FEED_IN(lsize);
|
||||
lsize = (lsize - 32) / 3;
|
||||
|
||||
if (len)
|
||||
for (n = 0; n < lsize; n++)
|
||||
{
|
||||
int x, y, z, w;
|
||||
FEED_IN(w); w -= 32;
|
||||
FEED_IN(x); x -= 32;
|
||||
FEED_IN(y); y -= 32;
|
||||
FEED_IN(z); z -= 32;
|
||||
|
||||
uchar a, b, c;
|
||||
int f = (w << 18) + (x << 12) + (y << 6) + z;
|
||||
a = (f >> 16) & 255;
|
||||
b = (f >> 8) & 255;
|
||||
c = f & 255;
|
||||
|
||||
FEED_OUT(a);
|
||||
FEED_OUT(b);
|
||||
FEED_OUT(c);
|
||||
}
|
||||
|
||||
if (len)
|
||||
FEED_IN(n); // Line jump.
|
||||
}
|
||||
return olen;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
|
||||
From yEnc.org (revision 1.3)
|
||||
----------------------------
|
||||
|
||||
A typical encoding process might look something like this:
|
||||
|
||||
1. Fetch a character from the input stream.
|
||||
2. Increment the character's ASCII value by 42, modulo 256
|
||||
3. If the result is a critical character (as defined in the previous
|
||||
section), write the escape character to the output stream and increment
|
||||
character's ASCII value by 64, modulo 256.
|
||||
4. Output the character to the output stream.
|
||||
5. Repeat from start.
|
||||
|
||||
(...)
|
||||
Under special circumstances, a single escape character (ASCII 3Dh, "=") is
|
||||
used to indicate that the following output character is "critical", and
|
||||
requires special handling.
|
||||
|
||||
Critical characters include the following:
|
||||
|
||||
ASCII 00h (NULL)
|
||||
ASCII 0Ah (LF)
|
||||
ASCII 0Dh (CR)
|
||||
ASCII 3Dh (=)
|
||||
|
||||
> ASCII 09h (TAB) -- removed in version (1.2)
|
||||
|
||||
*/
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
uint nAsciiEncoder::yEncode(const uchar *in, size_t len, uchar *out, size_t max, uint line_length)
|
||||
{
|
||||
if (line_length <= 0)
|
||||
__ERR__(__LOG_E__ << "invalid line-feed size for yEncoding.\n", 0)
|
||||
|
||||
uint olen = 0;
|
||||
int cchr = (int)line_length;
|
||||
|
||||
while (len--)
|
||||
{
|
||||
// Line-feed.
|
||||
if (cchr <= 0)
|
||||
{
|
||||
FEED_OUT('\n')
|
||||
cchr = (int)line_length;
|
||||
}
|
||||
|
||||
// yEnc.
|
||||
int v = (int(*in++) + 42) % 256;
|
||||
|
||||
switch (v)
|
||||
{
|
||||
case 0x00:
|
||||
case 0x0a:
|
||||
case 0x0d:
|
||||
case 0x3d:
|
||||
FEED_OUT(0x3d)
|
||||
cchr--;
|
||||
v = (v + 64) % 256;
|
||||
break;
|
||||
}
|
||||
|
||||
FEED_OUT(v)
|
||||
cchr--;
|
||||
}
|
||||
return olen;
|
||||
}
|
||||
uint nAsciiEncoder::yDecode(const uchar *in, size_t len, uchar *out, size_t max)
|
||||
{
|
||||
uint olen = 0;
|
||||
while (len--)
|
||||
{
|
||||
int v = (int)*in++;
|
||||
|
||||
if (v == 0x0a)
|
||||
FEED_IN(v)
|
||||
if ((v == 0x0d) && (in[0] == 0x0a)) // [EJ support for Windows-style EOL]
|
||||
{
|
||||
++in;
|
||||
len--;
|
||||
FEED_IN(v)
|
||||
}
|
||||
|
||||
if (v == 0x3d)
|
||||
{
|
||||
FEED_IN(v)
|
||||
v = (v - 64) % 256;
|
||||
}
|
||||
FEED_OUT((v - 42) % 256)
|
||||
}
|
||||
return olen;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
189
include/framework/ascii/parser.cpp
Normal file
189
include/framework/ascii/parser.cpp
Normal file
@ -0,0 +1,189 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "ascii/parser.h"
|
||||
#include "ntypes.h"
|
||||
|
||||
using namespace GS;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool AsciiParser::IsUpperCase(const char c)
|
||||
{ return asbool((c >= 'A') && (c <= 'Z')); }
|
||||
const char *AsciiParser::RunToEOS(const char *s, const char *e)
|
||||
{
|
||||
while (s < e)
|
||||
{
|
||||
if (s[0] == '\\')
|
||||
s += 2; // Jump modifiers.
|
||||
else if (s[0] == '"')
|
||||
break;
|
||||
else s++;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
bool AsciiParser::IsConstantFloat(const char *s, const char *e)
|
||||
{
|
||||
const char *eoc = SkipEntry(s, e);
|
||||
if (s[0] == '-')
|
||||
{
|
||||
s++;
|
||||
eoc = SkipEntry(s, e);
|
||||
}
|
||||
while (s < eoc)
|
||||
{
|
||||
if ((s[0] == '.') || (s[0] == 'f'))
|
||||
return true;
|
||||
s++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
const char *AsciiParser::Find(const char *s, const char *e, char f)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
s = SkipSpace(s, e);
|
||||
if (s == e)
|
||||
return NULL;
|
||||
if (s[0] == '(')
|
||||
s = RunToEOG(s, e, '(', ')');
|
||||
else
|
||||
{
|
||||
if (s[0] == f)
|
||||
break;
|
||||
s++;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
const char *AsciiParser::SkipEntry(const char *s, const char *e, bool skip_minus)
|
||||
{
|
||||
{
|
||||
while (
|
||||
(s[0] != 0x20) &&
|
||||
!((s[0] == 0xd) && (s[1] == 0xa)) &&
|
||||
(s[0] != 0x9) &&
|
||||
(s[0] != 0xa) &&
|
||||
(s[0] != 0xd) &&
|
||||
(s[0] != '/') &&
|
||||
(s[0] != '*') &&
|
||||
(s[0] != '+') &&
|
||||
(s[0] != '=') &&
|
||||
(s[0] != ';') &&
|
||||
(s[0] != ':') &&
|
||||
(s[0] != ',') &&
|
||||
(s[0] != '<') &&
|
||||
(s[0] != '>') &&
|
||||
(s[0] != '(') &&
|
||||
(s[0] != ')') &&
|
||||
(s[0] != '\"')
|
||||
)
|
||||
{
|
||||
if (!skip_minus && (s[0] == '-'))
|
||||
break;
|
||||
if (s == e)
|
||||
break;
|
||||
s++;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
const char *AsciiParser::RunToEOL(const char *s, const char *e)
|
||||
{
|
||||
while (
|
||||
((s[0] != 0xd) || (s[1] != 0xa)) &&
|
||||
(s[0] != 0xa) &&
|
||||
(s[0] != 0xd) &&
|
||||
(s < e)
|
||||
)
|
||||
s++;
|
||||
|
||||
return s;
|
||||
}
|
||||
const char *AsciiParser::SkipEOL(const char *s, const char *e)
|
||||
{
|
||||
if ((s[0] == 0xd) && (s[1] == 0xa))
|
||||
s += 2;
|
||||
else if ((s[0] == 0xa) || (s[0] == 0xd))
|
||||
s++;
|
||||
return s > e ? e : s;
|
||||
}
|
||||
const char *AsciiParser::RunToEOG(const char *s, const char *e, char op, char cl)
|
||||
{
|
||||
uint pc = 0;
|
||||
s++;
|
||||
while (s < e)
|
||||
{
|
||||
if (s[0] == op)
|
||||
pc++;
|
||||
if (s[0] == cl)
|
||||
{
|
||||
if (!pc)
|
||||
break;
|
||||
pc--;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
if (s == e)
|
||||
return NULL;
|
||||
return s;
|
||||
}
|
||||
const char *AsciiParser::RunToEOC(const char *s, const char *e)
|
||||
{
|
||||
s += 2;
|
||||
while (((s[0] != '*') || (s[1] != '/')) && (s < e))
|
||||
s += ((s[0] == 0xd) && (s[1] == 0xa)) ? 2 : 1;
|
||||
return s >= e ? e : s + 2;
|
||||
}
|
||||
const char *AsciiParser::RunToEOE(const char *s, const char *e)
|
||||
{
|
||||
while (s < e)
|
||||
{
|
||||
s = SkipSpace(s, e);
|
||||
if (s[0] == '(')
|
||||
s = RunToEOG(s, e, '(', ')');
|
||||
|
||||
else
|
||||
if (s[0] == '\"')
|
||||
{
|
||||
s++;
|
||||
while ((s < e) && (s[0] != '\"'))
|
||||
s++;
|
||||
if (s < e)
|
||||
s++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((s[0] == ',') || (s[0] == ';') )
|
||||
break;
|
||||
s++;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
const char *AsciiParser::SkipSpace(const char *s, const char *e)
|
||||
{
|
||||
while (s < e)
|
||||
{
|
||||
if (s[0] == 0x20) s++;
|
||||
else if ((s[0] == 0xd) && (s[1] == 0xa)) s += 2;
|
||||
else if ((s[0] == '/') && (s[1] == '/')) s = RunToEOL(s, e);
|
||||
else if ((s[0] == '/') && (s[1] == '*')) s = RunToEOC(s, e);
|
||||
else if (s[0] == 0x9) s++;
|
||||
else if (s[0] == 0xa) s++;
|
||||
else if (s[0] == 0xd) s++;
|
||||
// else if (s[0] == -17 && s[1] == -69 && s[2] == -65) s+=3; // remove the BOM from utf 8 file
|
||||
else break;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
const char *AsciiParser::NextEntry(const char *s, const char *e, bool skip_minus)
|
||||
{
|
||||
s = SkipEntry(s, e, skip_minus);
|
||||
s = SkipSpace(s, e);
|
||||
return s;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
52
include/framework/audio/audio_io.cpp
Normal file
52
include/framework/audio/audio_io.cpp
Normal file
@ -0,0 +1,52 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "audio/audio_io.h"
|
||||
#include "filesystem/filesystem.h"
|
||||
#include "platform.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
|
||||
template<> AudioIO *Singleton <AudioIO> ::i = NULL;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void AudioIO::RegisterStreamFactory(IAudioStreamFactory *f)
|
||||
{ stream_factories.Add(f); }
|
||||
void AudioIO::RegisterSampleFactory(ISampleFactory *f)
|
||||
{ sample_factories.Add(f); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
ISample *AudioIO::LoadSample(const char *path, const char *format)
|
||||
{
|
||||
String fmt(format);
|
||||
if (Platform::Get().io->Exists(path))
|
||||
ListForeachPtr(ISampleFactory *, codec, sample_factories)
|
||||
if (ISample *sample = codec->Load(path))
|
||||
{
|
||||
if (!fmt || (fmt == sample->GetFormat()))
|
||||
return sample;
|
||||
_safe_delete(sample);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
IAudioStream *AudioIO::OpenStream(const char *path, const char *format)
|
||||
{
|
||||
String fmt(format);
|
||||
if (Platform::Get().io->Exists(path))
|
||||
ListForeachPtr(IAudioStreamFactory *, codec, stream_factories)
|
||||
if (IAudioStream *stream = codec->Open(path))
|
||||
{
|
||||
if (!fmt || (fmt == stream->GetFormat()))
|
||||
return stream;
|
||||
_safe_delete(stream);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
71
include/framework/audio/sample_stream_factory.cpp
Normal file
71
include/framework/audio/sample_stream_factory.cpp
Normal file
@ -0,0 +1,71 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "audio/sample_stream_factory.h"
|
||||
#include "audio/sample_wav.h"
|
||||
#include "audio/audio_io.h"
|
||||
#include "audio/stream_interface.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
ISample *SampleStreamFactory::Load(const char *path)
|
||||
{
|
||||
// Open stream...
|
||||
AutoPtr <IAudioStream> stream(AudioIO::Get().OpenStream(path));
|
||||
if (stream.IsNull())
|
||||
return NULL;
|
||||
|
||||
#define PCM_OUTPUT_GROW_STEP 16384 // PCM output grows 16k at a time.
|
||||
|
||||
Array <char> data, temp(stream->GetPCMBufferSize());
|
||||
size_t pcm_size = 0;
|
||||
|
||||
// ...decode and dump PCM content to buffer.
|
||||
forever
|
||||
{
|
||||
size_t avail = stream->GetPCM(temp.c_ptr());
|
||||
if (!avail)
|
||||
{
|
||||
if (stream->IsEOF())
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t r_size = pcm_size + avail;
|
||||
if (r_size > data.GetSize())
|
||||
{
|
||||
size_t size = (r_size / PCM_OUTPUT_GROW_STEP + 1) * PCM_OUTPUT_GROW_STEP;
|
||||
|
||||
if (!data.Reallocate(size)) // no way to know the PCM output size, this is bad for memory fragmentation...
|
||||
{
|
||||
__LOG_W__ << "Failed to append pcm chunk to sample, output will be truncated.\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Memory::Copy(&data[(int)pcm_size], temp.c_ptr(), avail);
|
||||
pcm_size += avail;
|
||||
}
|
||||
|
||||
if (pcm_size == 0)
|
||||
return NULL;
|
||||
|
||||
// Commit to sample object.
|
||||
__LOG__ << "OGG '" << path << "' -> PCM data size: " << pcm_size << " bytes.\n";
|
||||
uint sample_count = pcm_size / (stream->format.channels * stream->format.resolution / 8);
|
||||
|
||||
//
|
||||
AutoPtr <SampleWav> sample(new SampleWav);
|
||||
if (sample.IsNull())
|
||||
return NULL;
|
||||
|
||||
sample->Set(data, sample_count, stream->format);
|
||||
return sample.Detach();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
44
include/framework/audio/sample_wav.cpp
Normal file
44
include/framework/audio/sample_wav.cpp
Normal file
@ -0,0 +1,44 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "audio/sample_wav.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool SampleWav::GetSampleFormat(SampleFormat &fmt) const
|
||||
{
|
||||
fmt = format;
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Time SampleWav::GetDuration() const
|
||||
{ return Time::fromMs(sample_count * 1000 / format.frequency); }
|
||||
uint SampleWav::GetPCMDataSize() const
|
||||
{ return format.GetPCMDataSize(sample_count); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
char *SampleWav::AllocAs(uint count, const SampleFormat &fmt)
|
||||
{
|
||||
format = fmt;
|
||||
if (!pcm_data.Allocate(format.GetPCMDataSize(count)))
|
||||
__ERR__(__LOG_E__ << "Failed to allocate raw PCM sample buffer.\n", NULL)
|
||||
|
||||
sample_count = count;
|
||||
return pcm_data;
|
||||
}
|
||||
void SampleWav::Set(Array <char> &pcm, uint count, const SampleFormat &fmt)
|
||||
{
|
||||
pcm_data = pcm;
|
||||
sample_count = count;
|
||||
format = fmt;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
119
include/framework/audio/sample_wav_factory.cpp
Normal file
119
include/framework/audio/sample_wav_factory.cpp
Normal file
@ -0,0 +1,119 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "audio/sample_wav_factory.h"
|
||||
#include "audio/sample_wav.h"
|
||||
#include "filesystem/filesystem.h"
|
||||
#include "filesystem/io_handle.h"
|
||||
#include "memory/endian.h"
|
||||
#include "platform.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
ISample *SampleWavFactory::Load(const char *path)
|
||||
{
|
||||
AutoPtr <IO::Handle> h(Platform::Get().io->Open(path));
|
||||
if (h.IsNull())
|
||||
return NULL;
|
||||
|
||||
// Verify format.
|
||||
char header[4];
|
||||
h->Read(header, 4);
|
||||
if (memcmp(header, "RIFF", 4))
|
||||
return NULL;
|
||||
h->Seek(4);
|
||||
h->Read(header, 4);
|
||||
if (memcmp(header, "WAVE", 4))
|
||||
return NULL;
|
||||
|
||||
// Create sample.
|
||||
AutoPtr <SampleWav> sample(new SampleWav);
|
||||
if (sample.IsNull())
|
||||
return NULL;
|
||||
|
||||
// Parse format.
|
||||
bool has_format = false, has_data = false;
|
||||
|
||||
struct Format
|
||||
{
|
||||
short wFormatTag;
|
||||
unsigned short wChannels;
|
||||
unsigned long dwSamplesPerSec;
|
||||
unsigned long dwAvgBytesPerSec;
|
||||
unsigned short wBlockAlign;
|
||||
unsigned short wBitsPerSample;
|
||||
};
|
||||
Format format;
|
||||
|
||||
Memory::Set(&format, 0, sizeof(Format));
|
||||
|
||||
forever
|
||||
{
|
||||
// Chunk + size.
|
||||
if (h->Read(header, 4) != 4)
|
||||
break;
|
||||
uint chunk_size = h->Read <uint> ();
|
||||
|
||||
// WAV format tag.
|
||||
if (!memcmp(header, "fmt ", 4))
|
||||
{
|
||||
uint cs = chunk_size;
|
||||
if (cs > sizeof(Format))
|
||||
{
|
||||
__LOG_W__ << "Unexpected WAV 'format' chunk size. Found " << cs << ", expected " << (int)sizeof(Format) << ".\n";
|
||||
cs = sizeof(Format);
|
||||
}
|
||||
if (h->Read(&format, cs) != cs)
|
||||
__ERR__(__LOG_E__ << "Mangled WAV 'format' chunk in '" << path << "'.\n", NULL)
|
||||
|
||||
Endian::ToHost(&format.wFormatTag, 2, Endian::Intel);
|
||||
Endian::ToHost(&format.wChannels, 2, Endian::Intel);
|
||||
Endian::ToHost(&format.dwSamplesPerSec, 4, Endian::Intel);
|
||||
Endian::ToHost(&format.dwAvgBytesPerSec, 4, Endian::Intel);
|
||||
Endian::ToHost(&format.wBlockAlign, 2, Endian::Intel);
|
||||
Endian::ToHost(&format.wBitsPerSample, 2, Endian::Intel);
|
||||
|
||||
has_format = true;
|
||||
|
||||
// Finish skipping tag.
|
||||
if (cs != chunk_size)
|
||||
h->Seek(chunk_size - cs);
|
||||
}
|
||||
|
||||
// WAV data tag.
|
||||
else if (!memcmp(header, "data", 4))
|
||||
{
|
||||
if (has_format)
|
||||
{
|
||||
char *pcm = sample->AllocAs(chunk_size / (format.wBitsPerSample / 8) / format.wChannels, SampleFormat(SampleFormat::Format_PCM, format.wChannels, format.dwSamplesPerSec, (uchar)format.wBitsPerSample));
|
||||
if (!pcm)
|
||||
__ERR__(__LOG_E__ << "failed to allocate WAV data chunk for '" << path << "'.\n", NULL)
|
||||
if (h->Read(pcm, chunk_size) != chunk_size)
|
||||
__ERR__(__LOG_E__ << "mangled WAV 'data' chunk in '" << path << "'.\n", NULL)
|
||||
}
|
||||
else
|
||||
__ERR__(__LOG_E__ << "WAV data with no format in '" << path << "'.\n", NULL)
|
||||
|
||||
has_data = true;
|
||||
}
|
||||
else
|
||||
h->Seek(chunk_size);
|
||||
}
|
||||
|
||||
if (!has_format || !has_data)
|
||||
return NULL;
|
||||
|
||||
SampleFormat sample_format;
|
||||
if (!sample->GetSampleFormat(sample_format))
|
||||
return NULL;
|
||||
|
||||
__LOG__ << "Sample format: " << sample_format.frequency / 1000 << "KHz@" << sample_format.resolution << "bit, " << sample_format.channels << " channel(s).\n";
|
||||
return sample.Detach();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
34
include/framework/bih/bih.cpp
Normal file
34
include/framework/bih/bih.cpp
Normal file
@ -0,0 +1,34 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "bih/bih.h"
|
||||
|
||||
using namespace GS::BIH;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Node::~Node()
|
||||
{
|
||||
if (p)
|
||||
if (axis != Math::AxisNone)
|
||||
delete [] ((Node *)p);
|
||||
|
||||
p = 0;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Tree::Free()
|
||||
{
|
||||
root = NULL;
|
||||
sarray.Free();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Tree::Tree() : min_leaf_vcount(8) {}
|
||||
Tree::~Tree() { Free(); }
|
||||
//------------------------------------------------------------------------------
|
||||
160
include/framework/bih/bih_build.cpp
Normal file
160
include/framework/bih/bih_build.cpp
Normal file
@ -0,0 +1,160 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include <float.h>
|
||||
#include "bih/bih.h"
|
||||
#include "timing/benchmark.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::BIH;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
static void HalveMinMax(MinMax &minmax, int n, bool trim_max)
|
||||
{
|
||||
if (trim_max)
|
||||
minmax.mx[n] = (minmax.mn[n] + minmax.mx[n]) * 0.5f;
|
||||
else minmax.mn[n] = (minmax.mn[n] + minmax.mx[n]) * 0.5f;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void Tree::MakeNodeLeaf(Node *node, uint count, uint *p_sarray, MinMax * /*varray*/)
|
||||
{
|
||||
leaf_count++;
|
||||
node->axis = Math::AxisNone;
|
||||
node->p = (void *)p_sarray;
|
||||
node->count = count;
|
||||
}
|
||||
void Tree::DoNodeSplit(MinMax &minmax, uint count, uint *sarray, MinMax *varray, uint &pivot, Node *node, uint &split_axis)
|
||||
{
|
||||
// Determine split axis.
|
||||
Vector4 dt = minmax.mx - minmax.mn;
|
||||
|
||||
if ((dt.x > dt.y) && (dt.x > dt.z))
|
||||
split_axis = 0;
|
||||
else if ((dt.y > dt.x) && (dt.y > dt.z))
|
||||
split_axis = 1;
|
||||
else
|
||||
split_axis = 2;
|
||||
|
||||
float split_coord = (minmax.mn[split_axis] + minmax.mx[split_axis]) * 0.5f;
|
||||
|
||||
// Fill split arrays.
|
||||
float extends[2];
|
||||
uint high = count;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
#define __INDICE_SWAP__(LO, HI) { uint swp = sarray[LO]; sarray[LO] = sarray[HI]; sarray[HI] = swp; }
|
||||
//--------------------------------------------------------------------------
|
||||
#define __GET_EXTENDS__(I, S) { extends[0] = varray[sarray[I]].mn[S]; extends[1] = varray[sarray[I]].mx[S]; }
|
||||
|
||||
pivot = 0;
|
||||
while (pivot < high)
|
||||
{
|
||||
__GET_EXTENDS__(pivot, split_axis)
|
||||
if ((extends[1] - split_coord) > (split_coord - extends[0]))
|
||||
{ // max
|
||||
__INDICE_SWAP__(pivot, high - 1)
|
||||
high--;
|
||||
}
|
||||
else
|
||||
{ // min
|
||||
__INDICE_SWAP__(0, pivot)
|
||||
pivot++;
|
||||
}
|
||||
}
|
||||
|
||||
// Node extends.
|
||||
node->split[0] = -FLT_MAX;
|
||||
|
||||
uint n;
|
||||
for (n = 0; n < pivot; ++n)
|
||||
{
|
||||
__GET_EXTENDS__(n, split_axis)
|
||||
if (extends[1] > node->split[0])
|
||||
node->split[0] = extends[1] + 0.0001f;
|
||||
}
|
||||
node->split[1] = FLT_MAX;
|
||||
for (; n < count; ++n)
|
||||
{
|
||||
__GET_EXTENDS__(n, split_axis)
|
||||
if (extends[0] < node->split[1])
|
||||
node->split[1] = extends[0] - 0.0001f;
|
||||
}
|
||||
}
|
||||
bool Tree::Split(MinMax &l_minmax, uint count, uint *p_sarray, MinMax *varray, Node *node, uint dpth)
|
||||
{
|
||||
if ((count <= min_leaf_vcount) || (dpth == 64))
|
||||
{
|
||||
if (dpth > depth)
|
||||
depth = dpth;
|
||||
MakeNodeLeaf(node, count, p_sarray, varray);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Split node.
|
||||
uint pivot, split_axis;
|
||||
DoNodeSplit(l_minmax, count, p_sarray, varray, pivot, node, split_axis);
|
||||
|
||||
// Distribute to children.
|
||||
node_count += 2;
|
||||
Node *children = new Node[2];
|
||||
if (!children)
|
||||
__ERR__(__LOG_E__ << "Failed to allocate BIH node children.\n", false)
|
||||
node->axis = (char)split_axis;
|
||||
node->p = (void *)children;
|
||||
|
||||
MinMax minmax_child = l_minmax;
|
||||
HalveMinMax(minmax_child, split_axis, true);
|
||||
Split(minmax_child, pivot, p_sarray, varray, &children[0], dpth + 1);
|
||||
minmax_child = l_minmax;
|
||||
HalveMinMax(minmax_child, split_axis, false);
|
||||
Split(minmax_child, count - pivot, &p_sarray[pivot], varray, &children[1], dpth + 1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool Tree::Build(uint count, MinMax *varray)
|
||||
{
|
||||
Benchmark build_bench(true);
|
||||
|
||||
if (!count)
|
||||
return false;
|
||||
|
||||
// Initialize split array.
|
||||
if (!sarray.Allocate(count))
|
||||
__ERR__(__LOG_E__ << "Failed to allocate BIH indice array.\n", false)
|
||||
|
||||
uint n;
|
||||
for (n = 0; n < count; ++n)
|
||||
sarray[n] = n;
|
||||
|
||||
// Get volume set bounding coordinates.
|
||||
minmax = varray[0];
|
||||
for (n = 1; n < count; ++n)
|
||||
minmax.Grow(varray[n]);
|
||||
minmax.mn -= 0.0001f;
|
||||
minmax.mx += 0.0001f;
|
||||
|
||||
// Split.
|
||||
leaf_count = 0;
|
||||
node_count = 1;
|
||||
depth = 0;
|
||||
|
||||
if (!(root = new Node))
|
||||
__ERR__(__LOG_E__ << "Failed to allocate BIH root node.\n", false)
|
||||
|
||||
bool success = Split(minmax, count, sarray, varray, root, 0);
|
||||
|
||||
build_bench.Stop();
|
||||
// __LOG__ << "Done in " << build_bench.GetLastStepMs() << "ms. " << node_count << " nodes, " << leaf_count << " leaves, depth = " << depth << ".\n";
|
||||
return success;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
55
include/framework/bih/bih_intersect.cpp
Normal file
55
include/framework/bih/bih_intersect.cpp
Normal file
@ -0,0 +1,55 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "bih/bih.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::BIH;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
uint Tree::IntersectNode(Node *node, MinMax &mm, uint *iarray, uint max)
|
||||
{
|
||||
uint count = 0;
|
||||
|
||||
if (node->axis == 3)
|
||||
{
|
||||
if (node->count > max)
|
||||
return 0;
|
||||
|
||||
Memory::Copy(iarray, (uint *)node->p, sizeof(uint) * node->count);
|
||||
return node->count;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mm.mx[node->axis] > node->split[1])
|
||||
{
|
||||
MinMax sub_mm = mm;
|
||||
if (node->split[1] > sub_mm.mn[node->axis])
|
||||
sub_mm.mn[node->axis] = node->split[1];
|
||||
|
||||
uint added = IntersectNode(&((Node *)node->p)[1], sub_mm, iarray/* + count*/, max);
|
||||
max -= added; count += added;
|
||||
}
|
||||
if (mm.mn[node->axis] < node->split[0])
|
||||
{
|
||||
MinMax sub_mm = mm;
|
||||
if (node->split[0] < sub_mm.mx[node->axis])
|
||||
sub_mm.mx[node->axis] = node->split[0];
|
||||
|
||||
uint added = IntersectNode(&((Node *)node->p)[0], sub_mm, iarray + count, max);
|
||||
/*max -= added;*/ count += added;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
uint Tree::Intersect(MinMax &in_mm, uint *iarray, uint max)
|
||||
{
|
||||
if (root.IsNull() || !in_mm.TestOverlap(minmax))
|
||||
return 0;
|
||||
return IntersectNode(root, in_mm, iarray, max);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
101
include/framework/bih/bih_trace.cpp
Normal file
101
include/framework/bih/bih_trace.cpp
Normal file
@ -0,0 +1,101 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "bih/bih.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::BIH;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Tree::Raytrace(Trace &trace, const Vector4 &s, const Vector4 &d, float l, void *parm)
|
||||
{
|
||||
trace.has_i = false;
|
||||
trace.i_t = -1;
|
||||
trace.node_visited = 0;
|
||||
trace.stack_pos = 0;
|
||||
|
||||
// Intersect BIH bounding volume.
|
||||
float tmin, tmax;
|
||||
if (!minmax.IntersectRay(s, d, tmin, tmax))
|
||||
return;
|
||||
|
||||
// Reject if intersection is too far away.
|
||||
if ((l > 0) && (tmin >= l))
|
||||
return;
|
||||
|
||||
// Initialize trace.
|
||||
trace.s = s;
|
||||
trace.d = d;
|
||||
|
||||
tmax = ((l > 0) && (tmax > l)) ? l : tmax;
|
||||
|
||||
// Iterative trace.
|
||||
float i_t[2];
|
||||
|
||||
for (Node *node = root; node; )
|
||||
{
|
||||
if (!trace.has_i || ((tmin < trace.i_t) && trace.want_closest)) // Only bother about rays that could lead to a closer hit.
|
||||
{
|
||||
while (node->axis != 3)
|
||||
{
|
||||
if (d[node->axis] == 0) // Axis aligned.
|
||||
{
|
||||
if (node->split[0] > s[node->axis])
|
||||
{
|
||||
if (s[node->axis] > node->split[1])
|
||||
{
|
||||
trace.stack[trace.stack_pos].node = &((Node *)node->p)[1];
|
||||
trace.stack[trace.stack_pos].tmin = tmin;
|
||||
trace.stack[trace.stack_pos++].tmax = tmax;
|
||||
}
|
||||
node = &((Node *)node->p)[0];
|
||||
}
|
||||
else if (s[node->axis] > node->split[1])
|
||||
node = &((Node *)node->p)[1];
|
||||
else break; // Empty space.
|
||||
}
|
||||
else
|
||||
{
|
||||
float idn = 1.f / d[node->axis];
|
||||
i_t[0] = (node->split[0] - s[node->axis]) * idn;
|
||||
i_t[1] = (node->split[1] - s[node->axis]) * idn;
|
||||
|
||||
int min = d[node->axis] > 0 ? 0 : 1, max = 1 - min;
|
||||
|
||||
if (i_t[min] > tmin)
|
||||
{
|
||||
if (tmax > i_t[max])
|
||||
{
|
||||
trace.stack[trace.stack_pos].node = &((Node *)node->p)[max];
|
||||
trace.stack[trace.stack_pos].tmin = (i_t[max] > tmin) ? i_t[max] : tmin;
|
||||
trace.stack[trace.stack_pos++].tmax = tmax;
|
||||
}
|
||||
node = &((Node *)node->p)[min];
|
||||
tmax = (i_t[min] < tmax) ? i_t[min] : tmax;
|
||||
}
|
||||
else if (tmax > i_t[max])
|
||||
{
|
||||
node = &((Node *)node->p)[max];
|
||||
tmin = (i_t[max] > tmin) ? i_t[max] : tmin;
|
||||
}
|
||||
else break; // Empty space.
|
||||
}
|
||||
trace.node_visited++;
|
||||
}
|
||||
if (node->axis == 3)
|
||||
TraceLeaf(node, tmin, tmax, trace, parm);
|
||||
}
|
||||
|
||||
if (!trace.stack_pos)
|
||||
break;
|
||||
|
||||
node = trace.stack[--trace.stack_pos].node;
|
||||
tmin = trace.stack[trace.stack_pos].tmin;
|
||||
tmax = trace.stack[trace.stack_pos].tmax;
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
53
include/framework/color/color.cpp
Normal file
53
include/framework/color/color.cpp
Normal file
@ -0,0 +1,53 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "color/color.h"
|
||||
|
||||
using namespace GS;
|
||||
|
||||
Color Color::White(1, 1, 1),
|
||||
Color::Grey(0.5, 0.5, 0.5),
|
||||
Color::Black(0, 0, 0),
|
||||
Color::Red(1, 0, 0),
|
||||
Color::Green(0, 1, 0),
|
||||
Color::Blue(0, 0, 1),
|
||||
Color::Yellow(1, 1, 0),
|
||||
Color::Purple(1, 0, 1);
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
uint Color::AsInteger() const
|
||||
{
|
||||
uint value;
|
||||
uchar *pl = (uchar *)&(value);
|
||||
#if (__PLATFORM_WINDOWS__ || __PLATFORM_LINUX__ || __PLATFORM_NINTENDO_WII__)
|
||||
float tmp_x = (x * 255.f) + 256.f,
|
||||
tmp_y = (y * 255.f) + 256.f,
|
||||
tmp_z = (z * 255.f) + 256.f,
|
||||
tmp_w = (w * 255.f) + 256.f;
|
||||
|
||||
pl[0] = (uchar)((((int &)tmp_x) & 0x7fffff) >> 15);
|
||||
pl[1] = (uchar)((((int &)tmp_y) & 0x7fffff) >> 15);
|
||||
pl[2] = (uchar)((((int &)tmp_z) & 0x7fffff) >> 15);
|
||||
pl[3] = (uchar)((((int &)tmp_w) & 0x7fffff) >> 15);
|
||||
#else
|
||||
pl[0] = (uchar)(Types::Clamp(x, 0.f, 1.f) * 255.f);
|
||||
pl[1] = (uchar)(Types::Clamp(y, 0.f, 1.f) * 255.f);
|
||||
pl[2] = (uchar)(Types::Clamp(z, 0.f, 1.f) * 255.f);
|
||||
pl[3] = (uchar)(Types::Clamp(w, 0.f, 1.f) * 255.f);
|
||||
#endif
|
||||
return value;
|
||||
}
|
||||
void Color::FromInteger(uint value)
|
||||
{
|
||||
const uchar *pl = (const uchar *)&(value);
|
||||
const float i255 = 1.f / 255.f;
|
||||
x = (float)(pl[0]) * i255;
|
||||
y = (float)(pl[1]) * i255;
|
||||
z = (float)(pl[2]) * i255;
|
||||
w = (float)(pl[3]) * i255;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
280
include/framework/data/nvariant.cpp
Normal file
280
include/framework/data/nvariant.cpp
Normal file
@ -0,0 +1,280 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
------------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
#include <cstring>
|
||||
#include "data/nvariant.h"
|
||||
#include "alloc/ialloc.h"
|
||||
|
||||
using namespace GS;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Variant::Reset()
|
||||
{
|
||||
type = VariantNone;
|
||||
}
|
||||
void Variant::Free()
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case VariantString:
|
||||
s_value.Clear();
|
||||
break;
|
||||
|
||||
case VariantBinary:
|
||||
_safe_delete_array(d_value);
|
||||
d_size = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
type = VariantNone;
|
||||
}
|
||||
Variant::~Variant()
|
||||
{ Free(); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Variant &Variant::operator = (const Variant &v)
|
||||
{
|
||||
switch (v.GetType())
|
||||
{
|
||||
case VariantBool: *this = v.b_value; break;
|
||||
case VariantInteger: *this = v.i_value; break;
|
||||
case VariantFloat: *this = v.f_value; break;
|
||||
case VariantString: *this = v.s_value; break;
|
||||
case VariantBinary:
|
||||
{
|
||||
Free();
|
||||
void *v_data; size_t v_size;
|
||||
if (v.GetBinary(v_data, v_size))
|
||||
SetBinary(v_data, v_size);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
Free();
|
||||
break;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Variant::operator < (const Variant &b) const
|
||||
{
|
||||
switch (GetType())
|
||||
{
|
||||
case VariantBool:
|
||||
if (b.GetType() == VariantBool)
|
||||
return b_value < b.b_value;
|
||||
break;
|
||||
|
||||
case VariantInteger:
|
||||
switch (b.GetType())
|
||||
{
|
||||
case VariantInteger: return i_value < b.i_value;
|
||||
case VariantFloat: return i_value < (int)b.f_value;
|
||||
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
|
||||
case VariantFloat:
|
||||
switch (b.GetType())
|
||||
{
|
||||
case VariantInteger: return f_value < (float)b.i_value;
|
||||
case VariantFloat: return f_value < b.f_value;
|
||||
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool Variant::operator > (const Variant &b) const
|
||||
{ return !(*this < b); }
|
||||
bool Variant::operator == (const Variant &b) const
|
||||
{
|
||||
switch (GetType())
|
||||
{
|
||||
case VariantBool:
|
||||
if (b.GetType() == VariantBool)
|
||||
return b_value == b.b_value;
|
||||
break;
|
||||
|
||||
case VariantInteger:
|
||||
switch (b.GetType())
|
||||
{
|
||||
case VariantInteger: return i_value == b.i_value;
|
||||
case VariantFloat: return i_value == (int)b.f_value;
|
||||
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
|
||||
case VariantFloat:
|
||||
switch (b.GetType())
|
||||
{
|
||||
case VariantInteger: return f_value == (float)b.i_value;
|
||||
case VariantFloat: return f_value == b.f_value;
|
||||
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
|
||||
case VariantString:
|
||||
if (b.GetType() == VariantString)
|
||||
return s_value == b.s_value;
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool Variant::operator != (const Variant &b) const
|
||||
{ return !(*this == b); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Variant &Variant::operator = (const char *v)
|
||||
{
|
||||
Free();
|
||||
s_value.Set(v);
|
||||
type = VariantString;
|
||||
return *this;
|
||||
}
|
||||
bool Variant::Get(const char * &v) const
|
||||
{
|
||||
if (type != VariantString)
|
||||
return false;
|
||||
v = s_value.c_str();
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Variant &Variant::operator = (int v)
|
||||
{
|
||||
Free();
|
||||
type = VariantInteger;
|
||||
i_value = v;
|
||||
return *this;
|
||||
}
|
||||
bool Variant::Get(int &v) const
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case VariantBool: v = b_value ? 1 : 0; return true;
|
||||
case VariantInteger: v = i_value; return true;
|
||||
case VariantFloat: v = (int)f_value; return true;
|
||||
|
||||
default: break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Variant &Variant::operator = (uint v)
|
||||
{
|
||||
Free();
|
||||
type = VariantInteger;
|
||||
u_value = v;
|
||||
return *this;
|
||||
}
|
||||
bool Variant::Get(uint &v) const
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case VariantBool: v = b_value ? 1 : 0; return true;
|
||||
case VariantInteger: v = u_value; return true;
|
||||
case VariantFloat: v = (int)f_value; return true;
|
||||
|
||||
default: break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Variant &Variant::operator = (bool v)
|
||||
{
|
||||
Free();
|
||||
type = VariantBool;
|
||||
b_value = v;
|
||||
return *this;
|
||||
}
|
||||
bool Variant::Get(bool &v) const
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case VariantBool: v = b_value; return true;
|
||||
case VariantInteger: v = asbool(i_value); return true;
|
||||
case VariantFloat: v = asbool(f_value); return true;
|
||||
|
||||
default: break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Variant &Variant::operator = (float v)
|
||||
{
|
||||
Free();
|
||||
type = VariantFloat;
|
||||
f_value = v;
|
||||
return *this;
|
||||
}
|
||||
bool Variant::Get(float &v) const
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case VariantBool: v = b_value ? 1.f : 0.f; return true;
|
||||
case VariantInteger: v = (float)i_value; return true;
|
||||
case VariantFloat: v = f_value; return true;
|
||||
|
||||
default: break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Variant::SetBinary(const void *v, size_t size)
|
||||
{
|
||||
Free();
|
||||
|
||||
d_value = new char[size];
|
||||
if (d_value)
|
||||
{
|
||||
memcpy(d_value, v, size);
|
||||
d_size = size;
|
||||
type = VariantBinary;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool Variant::GetBinary(void *&data, size_t &size) const
|
||||
{
|
||||
if (type != VariantBinary)
|
||||
return false;
|
||||
|
||||
data = (void *)new char[d_size];
|
||||
if (data == NULL)
|
||||
{
|
||||
size = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(data, d_value, d_size);
|
||||
size = d_size;
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
86
include/framework/data/registry.cpp
Normal file
86
include/framework/data/registry.cpp
Normal file
@ -0,0 +1,86 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "data/registry.h"
|
||||
|
||||
using namespace GS;
|
||||
using GS::NML::Tag;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Tag *Registry::CreateKey(const char *path, const Variant *value, bool /*recursive*/)
|
||||
{
|
||||
StringList tag_path_list;
|
||||
String(path).TrimChar(';').Split(":", tag_path_list);
|
||||
|
||||
Tag *ctag = NULL;
|
||||
for (uint n = 0; n < tag_path_list.GetCount(); ++n)
|
||||
{
|
||||
String &tag_path = tag_path_list.ObjectAt(n);
|
||||
|
||||
Tag *ntag = ctag ? ctag->GetTag(tag_path) : GetTag(tag_path);
|
||||
if (ntag == NULL)
|
||||
ntag = ctag ? ctag->AddChild(tag_path) : AddRoot(tag_path);
|
||||
if ((ctag = ntag) == NULL)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ctag && value)
|
||||
{
|
||||
ctag->GetValue() = *value;
|
||||
RegistryKeyChange msg(path);
|
||||
BroadcastMessage(RegistryMsg_KeyChange, this, &msg);
|
||||
}
|
||||
return ctag;
|
||||
}
|
||||
Tag *Registry::CreateKey(const char *path, const Variant &value, bool recursive)
|
||||
{ return CreateKey(path, &value, recursive); }
|
||||
bool Registry::DeleteKey(const char *path)
|
||||
{
|
||||
StringList tag_path_list;
|
||||
String(path).Split(":", tag_path_list);
|
||||
|
||||
Tag *ctag = NULL, *ptag = NULL;
|
||||
for (uint n = 0; n < tag_path_list.GetCount(); ++n)
|
||||
{
|
||||
ptag = ctag;
|
||||
String &tag_path = tag_path_list.ObjectAt(n);
|
||||
Tag *ntag = ctag ? ctag->GetTag(tag_path) : GetTag(tag_path);
|
||||
if (!ntag)
|
||||
return false;
|
||||
ctag = ntag;
|
||||
}
|
||||
if (!ctag)
|
||||
return false;
|
||||
|
||||
if (ptag)
|
||||
ptag->RemoveTag(ctag);
|
||||
else
|
||||
tags.Remove(ctag);
|
||||
|
||||
_safe_delete(ctag);
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
float Registry::GetReal(const char *path, float default_value) const
|
||||
{
|
||||
Tag *tag = GetTag(path);
|
||||
float v;
|
||||
if (!tag || !tag->GetValue().Get(v))
|
||||
return default_value;
|
||||
return v;
|
||||
}
|
||||
bool Registry::GetBool(const char *path, bool default_value) const
|
||||
{
|
||||
Tag *tag = GetTag(path);
|
||||
bool v;
|
||||
if (!tag || !tag->GetValue().Get(v))
|
||||
return default_value;
|
||||
return v;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
93
include/framework/font/font_cache.cpp
Normal file
93
include/framework/font/font_cache.cpp
Normal file
@ -0,0 +1,93 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "font/font_cache.h"
|
||||
#include "font/font_extended.h"
|
||||
#include "filesystem/filesystem.h"
|
||||
#include "platform.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
FontEx *FontCache::GetFont(const char *name) const
|
||||
{
|
||||
ListForeachPtr(FontAlias *, alias, font_aliases)
|
||||
if (alias->font->GetName() == name)
|
||||
return alias->font;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
FontAlias *FontCache::GetAlias(const char *alias) const
|
||||
{
|
||||
ListForeachPtr(FontAlias *, font, font_aliases)
|
||||
if (font->alias == alias)
|
||||
return font;
|
||||
return NULL;
|
||||
}
|
||||
FontEx *FontCache::GetAliasedFont(const char *alias) const
|
||||
{
|
||||
FontAlias *fa = GetAlias(alias);
|
||||
return fa ? fa->font.c_ptr() : NULL;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
FontEx *FontCache::LoadFont(const char *path, const char *alias)
|
||||
{
|
||||
// Look for an already loaded instance of the font.
|
||||
FontEx *font = GetFont(path);
|
||||
|
||||
// If font is not available, load it.
|
||||
if (font == NULL)
|
||||
{
|
||||
IFont *base_font = font_factory->LoadFont(path);
|
||||
if (base_font == NULL)
|
||||
return NULL;
|
||||
|
||||
// Wrap with an extended font.
|
||||
font = new FontEx(base_font);
|
||||
}
|
||||
|
||||
// Format default alias if none provided.
|
||||
String _alias(path);
|
||||
_alias.FileCutPathAndExtension();
|
||||
if (!alias)
|
||||
alias = _alias;
|
||||
|
||||
// Drop current alias if existing.
|
||||
FontAlias *font_alias = GetAlias(alias);
|
||||
if (font_alias)
|
||||
font_aliases.Remove(font_alias);
|
||||
|
||||
// Create the alias.
|
||||
font_alias = new FontAlias;
|
||||
font_alias->alias = alias;
|
||||
font_alias->font = font;
|
||||
|
||||
font_aliases.Add(font_alias);
|
||||
|
||||
__LOG__ << "Created a new font alias from '" << path << "' to '" << alias << "'.\n";
|
||||
return font;
|
||||
}
|
||||
void FontCache::DeleteAlias(const char *alias)
|
||||
{
|
||||
ListForeachPtr(FontAlias *, a, font_aliases)
|
||||
if (a->alias == alias)
|
||||
font_aliases.Remove(a);
|
||||
}
|
||||
void FontCache::DeleteAllFont()
|
||||
{
|
||||
font_aliases.Clear();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
FontCache::~FontCache()
|
||||
{
|
||||
// [EJ] Get rid of the fonts before the factory.
|
||||
DeleteAllFont();
|
||||
}
|
||||
47
include/framework/font/font_extended.cpp
Normal file
47
include/framework/font/font_extended.cpp
Normal file
@ -0,0 +1,47 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "font/font_extended.h"
|
||||
|
||||
using namespace GS;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool FontEx::SetPixelSize(int size)
|
||||
{
|
||||
pixel_size = size;
|
||||
return current_glyph_font.IsValid() ? current_glyph_font->SetPixelSize(size) : font->SetPixelSize(size);
|
||||
}
|
||||
bool FontEx::HasKerning() const
|
||||
{ return current_glyph_font.IsValid() ? current_glyph_font->HasKerning() : font->HasKerning(); }
|
||||
int FontEx::GetKerning(uint previous_codepoint, uint codepoint) const
|
||||
{ return current_glyph_font.IsValid() ? current_glyph_font->GetKerning(previous_codepoint, codepoint) : font->GetKerning(previous_codepoint, codepoint); }
|
||||
|
||||
int FontEx::GetHeight() const
|
||||
{ return current_glyph_font.IsValid() ? current_glyph_font->GetHeight() : font->GetHeight(); }
|
||||
int FontEx::GetAdvance() const
|
||||
{ return current_glyph_font.IsValid() ? current_glyph_font->GetAdvance() : font->GetAdvance(); }
|
||||
|
||||
bool FontEx::LoadGlyph(uint codepoint, bool for_render)
|
||||
{
|
||||
current_glyph_font = font;
|
||||
if (font->LoadGlyph(codepoint, for_render))
|
||||
return true;
|
||||
|
||||
// Synchronize and try fallback.
|
||||
if (fallback.IsNull())
|
||||
return false;
|
||||
|
||||
fallback->SetPixelSize(pixel_size);
|
||||
bool r = fallback->LoadGlyph(codepoint, for_render);
|
||||
|
||||
if (r)
|
||||
current_glyph_font = fallback;
|
||||
return r;
|
||||
}
|
||||
bool FontEx::RenderCurrentGlyph(Picture &picture, const iPoint &position, const iRect &clip, const Color &color)
|
||||
{ return current_glyph_font.IsValid() ? current_glyph_font->RenderCurrentGlyph(picture, position, clip, color) : font->RenderCurrentGlyph(picture, position, clip, color); }
|
||||
//------------------------------------------------------------------------------
|
||||
377
include/framework/font/font_renderer.cpp
Normal file
377
include/framework/font/font_renderer.cpp
Normal file
@ -0,0 +1,377 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include <cstring>
|
||||
#include "font/font_renderer.h"
|
||||
#include "picture/pict.h"
|
||||
#include "ascii/parser.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::AsciiParser;
|
||||
|
||||
float FontRenderer::default_text_tracking = 0;
|
||||
float FontRenderer::default_text_heading = 0;
|
||||
|
||||
//
|
||||
struct GS::SubString
|
||||
{
|
||||
const char *entry;
|
||||
int char_count;
|
||||
int space_count;
|
||||
int width; // 26.6
|
||||
int height;
|
||||
|
||||
void Reset(const char *string)
|
||||
{
|
||||
entry = string;
|
||||
char_count = 0;
|
||||
space_count = 0;
|
||||
width = 0;
|
||||
height = 0;
|
||||
}
|
||||
};
|
||||
|
||||
int substring_count = 0;
|
||||
SubString substring_array[1024];
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
const char *FontRenderer::CheckCommand(const char *string, Command &command)
|
||||
{
|
||||
command.code = CommandNone;
|
||||
if (!string[0] || !string[1])
|
||||
return string;
|
||||
|
||||
if ((string[0] == '~') && (string[1] == '~'))
|
||||
{
|
||||
//----------------------------------------------------------------------
|
||||
#define PARSE_COMPONENT(_C_, _M_)\
|
||||
{\
|
||||
string = SkipSpace(string + 1, eos);\
|
||||
command.vector._C_ = (float)String::atoi(string);\
|
||||
string = NextEntry(string, eos);\
|
||||
if (string[0] != (_M_))\
|
||||
{\
|
||||
command.code = CommandParseError;\
|
||||
__LOG_E__ << "Error parsing text command components.\n";\
|
||||
return NULL;\
|
||||
}\
|
||||
}
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
const char *eos = string + std::strlen(string);
|
||||
|
||||
if (!strncmp("Color(", string + 2, 6) || !strncmp("COLOR(", string + 2, 6)) // [EJ] 1st may: range is [0;255]
|
||||
{
|
||||
command.code = CommandColor;
|
||||
string += 7;
|
||||
|
||||
const char *eop = Find(string, eos, ')');
|
||||
if (!eop)
|
||||
{
|
||||
command.code = CommandParseError;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PARSE_COMPONENT(x, ',');
|
||||
PARSE_COMPONENT(y, ',');
|
||||
PARSE_COMPONENT(z, ',');
|
||||
PARSE_COMPONENT(w, ')');
|
||||
string = eop + 1;
|
||||
}
|
||||
else if (!strncmp("Size(", string + 2, 5) || !strncmp("SIZE(", string + 2, 5))
|
||||
{
|
||||
command.code = CommandSize;
|
||||
|
||||
string += 6;
|
||||
|
||||
const char *eop = Find(string, eos, ')');
|
||||
if (!eop)
|
||||
{
|
||||
command.code = CommandParseError;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PARSE_COMPONENT(x, ')');
|
||||
string = eop + 1;
|
||||
}
|
||||
}
|
||||
return string;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
const char *FontRenderer::FetchSubString(const char *string, SubString &substring, TextState &state, int max_width, int max_char)
|
||||
{
|
||||
if (!string)
|
||||
return NULL;
|
||||
|
||||
// Jump over leading spaces.
|
||||
forever
|
||||
{
|
||||
if (string[0] != ' ')
|
||||
break;
|
||||
|
||||
if (!string[0] || (string[0] == '\n'))
|
||||
{
|
||||
substring.Reset(NULL);
|
||||
return string;
|
||||
}
|
||||
string++;
|
||||
}
|
||||
|
||||
// Start sub-string.
|
||||
substring.Reset(string);
|
||||
int previous_codepoint = 0;
|
||||
|
||||
state.font->SetPixelSize(state.GetSize());
|
||||
max_width <<= 6;
|
||||
|
||||
// Word cut point.
|
||||
bool rollback_available = false;
|
||||
SubString rollback_substring;
|
||||
const char *rollback_string = NULL;
|
||||
|
||||
int tracking = int(state.GetTracking() * 64.f);
|
||||
|
||||
forever
|
||||
{
|
||||
Command command;
|
||||
if ((string = CheckCommand(string, command)) == 0)
|
||||
break;
|
||||
|
||||
if (command.code == CommandNone)
|
||||
{
|
||||
if (string[0] == '\n')
|
||||
{
|
||||
string++;
|
||||
break;
|
||||
}
|
||||
if ((string[0] == '\\') && (string[1] == 'n'))
|
||||
{
|
||||
string += 2;
|
||||
break;
|
||||
}
|
||||
if (string[0] == 0)
|
||||
break;
|
||||
if ((max_char > 0) && (substring.char_count == max_char))
|
||||
break;
|
||||
|
||||
// Get glyph.
|
||||
uint codepoint;
|
||||
int codelength = String::Utf8toUtf32((const uchar *)string, &codepoint);
|
||||
|
||||
state.font->LoadGlyph(codepoint, false);
|
||||
|
||||
// Retrieve glyph formatting informations.
|
||||
int advance = state.font->GetAdvance();
|
||||
int kerning = (state.font->HasKerning() && (previous_codepoint != 0)) ? state.font->GetKerning(previous_codepoint, codepoint) : 0;
|
||||
|
||||
// Width constraint.
|
||||
if ((max_width > 0) && ((substring.width + advance) >= max_width))
|
||||
{
|
||||
rollback_available = true;
|
||||
break;
|
||||
}
|
||||
|
||||
substring.width += advance + kerning + tracking;
|
||||
substring.height = Types::Max(state.font->GetHeight(), substring.height);
|
||||
|
||||
// Count space.
|
||||
if (string[0] == ' ')
|
||||
{
|
||||
substring.space_count++;
|
||||
|
||||
// Store the word rollback position.
|
||||
Command dummy_command;
|
||||
CheckCommand(string, dummy_command);
|
||||
|
||||
if ((dummy_command.code == CommandNone) && (string[1] != ' '))
|
||||
{
|
||||
rollback_substring = substring;
|
||||
rollback_string = string;
|
||||
}
|
||||
}
|
||||
substring.char_count++;
|
||||
|
||||
// Next glyph.
|
||||
string += codelength ? codelength : 1;
|
||||
previous_codepoint = codepoint;
|
||||
}
|
||||
else
|
||||
switch (command.code)
|
||||
{
|
||||
case CommandColor: // Irrelevant when not composing.
|
||||
break;
|
||||
|
||||
case CommandSize:
|
||||
state.SetSize((int)command.vector.x);
|
||||
state.font->SetPixelSize(state.GetSize());
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
// Rollback to the last word position.
|
||||
if (rollback_available && rollback_string)
|
||||
{
|
||||
substring = rollback_substring;
|
||||
string = rollback_string;
|
||||
}
|
||||
return string;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void FontRenderer::DrawSubString(SubString &line, Picture &output, TextState &state, const iRect &_out_rect, const iRect &clip_rect, int justification)
|
||||
{
|
||||
const char *string = line.entry;
|
||||
state.font->SetPixelSize(state.GetSize());
|
||||
|
||||
iRect out_rect(_out_rect);
|
||||
uint previous_codepoint = 0;
|
||||
|
||||
int tracking = int(state.GetTracking() * 64.f);
|
||||
|
||||
for (int n = 0; n < line.char_count; ++n)
|
||||
{
|
||||
Command command;
|
||||
string = CheckCommand(string, command);
|
||||
|
||||
if (command.code == CommandNone)
|
||||
{
|
||||
// Get glyph.
|
||||
uint codepoint;
|
||||
int codelength = String::Utf8toUtf32((const uchar *)string, &codepoint);
|
||||
|
||||
state.font->LoadGlyph(codepoint, true);
|
||||
|
||||
// Retrieve glyph formatting informations.
|
||||
int advance = state.font->GetAdvance();
|
||||
int kerning = (state.font->HasKerning() && (previous_codepoint != 0)) ? state.font->GetKerning(previous_codepoint, codepoint) : 0;
|
||||
|
||||
// Render.
|
||||
int px = out_rect.sx >> 6;
|
||||
int py = (out_rect.sy + (line.height * 3) / 4) >> 6; // FIXME smells the hack... at best!
|
||||
|
||||
state.font->RenderCurrentGlyph(output, iPoint(px, py), clip_rect, state.color);
|
||||
|
||||
out_rect.sx += advance + kerning + tracking;
|
||||
|
||||
// Next glyph.
|
||||
string += codelength;
|
||||
previous_codepoint = codepoint;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (command.code)
|
||||
{
|
||||
case CommandColor:
|
||||
state.color = command.vector ;/// 255.f;
|
||||
break;
|
||||
|
||||
case CommandSize:
|
||||
state.SetSize((int)command.vector.x);
|
||||
state.font->SetPixelSize(state.GetSize());
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
--n;
|
||||
}
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
iRect FontRenderer::Format(const char *txt, const TextState &in_state, const iRect &out_rect)
|
||||
{
|
||||
if (!in_state.font)
|
||||
return iRect (0, 0, 0, 0);
|
||||
|
||||
// Create working state.
|
||||
TextState state = in_state;
|
||||
|
||||
// Setup formatting rules.
|
||||
int max_width = (state.format == TextState::Line) ? -1 : out_rect.GetWidth(),
|
||||
max_char = (state.format != TextState::Column) ? -1 : state.column_width;
|
||||
|
||||
// Fetch all substrings.
|
||||
substring_count = 0;
|
||||
while (txt && txt[0])
|
||||
txt = FetchSubString(txt, substring_array[substring_count++], state, max_width, max_char);
|
||||
|
||||
// Create full text rectangle.
|
||||
iRect text_rect;
|
||||
text_rect.Set(0, 0, 0, 0);
|
||||
|
||||
int leading = int(state.GetLeading() * 64);
|
||||
|
||||
for (int n = 0; n < substring_count; ++n)
|
||||
{
|
||||
if (text_rect.ex < substring_array[n].width)
|
||||
text_rect.ex = substring_array[n].width;
|
||||
text_rect.ey += substring_array[n].height + leading;
|
||||
}
|
||||
if (substring_count > 0)
|
||||
text_rect.ey -= leading;
|
||||
|
||||
text_rect.ex = text_rect.ex / 64;
|
||||
text_rect.ey = text_rect.ey / 64;
|
||||
return text_rect;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
iRect FontRenderer::Compose(Picture &output, const char *txt, const TextState &in_state, const iRect &out_rect, const iRect &clip_rect)
|
||||
{
|
||||
if (in_state.font.IsNull() || (output.GetPixelFormat().GetBpp() != 32))
|
||||
return iRect(0, 0, 0, 0);
|
||||
|
||||
// Create working state.
|
||||
TextState state = in_state;
|
||||
int leading = int(state.GetLeading() * 64);
|
||||
|
||||
// Draw substrings.
|
||||
iRect work_out_rect(out_rect * 64);
|
||||
|
||||
for (int n = 0; n < substring_count; ++n)
|
||||
{
|
||||
iRect line_rect(work_out_rect);
|
||||
line_rect.ex = line_rect.sx + substring_array[n].width;
|
||||
|
||||
int offset = 0, justification = 0;
|
||||
|
||||
switch (state.alignment)
|
||||
{
|
||||
case TextState::Left:
|
||||
offset = out_rect.sx * 64 - line_rect.sx;
|
||||
break;
|
||||
case TextState::Center:
|
||||
offset = (out_rect.GetWidth() * 64 - line_rect.GetWidth()) / 2;
|
||||
break;
|
||||
case TextState::Right:
|
||||
offset = out_rect.ex * 64 - line_rect.ex;
|
||||
break;
|
||||
|
||||
case TextState::Justify:
|
||||
if (n < (substring_count - 1))
|
||||
justification = (out_rect.GetWidth() * 64 - line_rect.GetWidth()) / substring_array[n].space_count;
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
line_rect.sx += offset;
|
||||
|
||||
DrawSubString(substring_array[n], output, state, line_rect, clip_rect, justification);
|
||||
work_out_rect.sy += substring_array[n].height + leading;
|
||||
}
|
||||
return out_rect;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
342
include/framework/geometry/bounding_box.cpp
Normal file
342
include/framework/geometry/bounding_box.cpp
Normal file
@ -0,0 +1,342 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include <float.h>
|
||||
#include "geometry/bounding_box.h"
|
||||
#include "metafile/nml.h"
|
||||
#include "math/matrix4.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::NML;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void OBB::Transform(const Matrix4 &mtx)
|
||||
{
|
||||
Matrix3 rmtx = Matrix3::FromMatrix4(mtx);
|
||||
bb_rotation = rmtx * bb_rotation;
|
||||
bb_position = bb_position * rmtx + mtx.GetRow(3);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void OBB::ComputeMinMax(MinMax &minmax)
|
||||
{
|
||||
Vector4 xtd(bb_scale * 0.5f);
|
||||
Vector4 smt[4];
|
||||
|
||||
smt[0].Set(xtd.x, xtd.y, xtd.z);
|
||||
smt[1].Set(-xtd.x, xtd.y, xtd.z);
|
||||
smt[2].Set(xtd.x, -xtd.y, xtd.z);
|
||||
smt[3].Set(xtd.x, xtd.y, -xtd.z);
|
||||
|
||||
int n;
|
||||
for (n = 0; n < 4; n++)
|
||||
smt[n] = (smt[n] * bb_rotation).Abs();
|
||||
|
||||
minmax.mx = smt[0];
|
||||
for (n = 1; n < 4; n++)
|
||||
{
|
||||
if (smt[n].x > minmax.mx.x) minmax.mx.x = smt[n].x;
|
||||
if (smt[n].y > minmax.mx.y) minmax.mx.y = smt[n].y;
|
||||
if (smt[n].z > minmax.mx.z) minmax.mx.z = smt[n].z;
|
||||
}
|
||||
minmax.mn.x = -minmax.mx.x;
|
||||
minmax.mn.y = -minmax.mx.y;
|
||||
minmax.mn.z = -minmax.mx.z;
|
||||
|
||||
minmax.mn += bb_position;
|
||||
minmax.mx += bb_position;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool OBB::FromMetaTag(Tag &tag)
|
||||
{
|
||||
Tag *t;
|
||||
List <Tag *> ::Iterator i(tag.GetTags().GetRoot());
|
||||
|
||||
t = i.ObjectPtr();
|
||||
if (!t) return false;
|
||||
bb_position.FromMetaTag(*t);
|
||||
++i;
|
||||
|
||||
t = i.ObjectPtr();
|
||||
if (!t) return false;
|
||||
bb_scale.FromMetaTag(*t);
|
||||
++i;
|
||||
|
||||
t = i.ObjectPtr();
|
||||
if (!t) return false;
|
||||
bb_rotation.FromMetaTag(*t);
|
||||
return true;
|
||||
}
|
||||
Tag *OBB::AsMetaTag()
|
||||
{
|
||||
Tag *root = new Tag("OBB");
|
||||
if (root)
|
||||
{
|
||||
root->AddChild(bb_position.AsMetaTag("Position"));
|
||||
root->AddChild(bb_scale.AsMetaTag("Scale"));
|
||||
root->AddChild(bb_rotation.AsMetaTag("Matrix"));
|
||||
}
|
||||
return root;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool MinMax::IntersectRay(const Vector4 &o, const Vector4 &d, float &tmin, float &tmax)
|
||||
{
|
||||
tmin = 0;
|
||||
tmax = FLT_MAX;
|
||||
|
||||
for (uint n = 0; n < 3; ++n)
|
||||
if (Math::EqualZero(d[n]))
|
||||
{
|
||||
if ((o[n] < mn[n]) || (o[n] > mx[n]))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
float ood = 1.f / d[n];
|
||||
float t0 = (mn[n] - o[n]) * ood;
|
||||
float t1 = (mx[n] - o[n]) * ood;
|
||||
|
||||
if (t0 > t1)
|
||||
{ float swp = t1; t1 = t0; t0 = swp; }
|
||||
|
||||
tmin = tmin < t0 ? t0 : tmin;
|
||||
tmax = tmax < t1 ? tmax : t1;
|
||||
|
||||
if (tmin > tmax)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool MinMax::ClassifyLine(const Vector4 &p1, const Vector4 &direction, Vector4 &itr, Vector4 *n) const
|
||||
{
|
||||
uint oc1, oc2;
|
||||
|
||||
oc1 = cc_oc(mn, mx, p1);
|
||||
if (oc1 == ClipNone)
|
||||
{
|
||||
// Point inside bounding box.
|
||||
if (n)
|
||||
n->Set(0, 0, 0);
|
||||
itr = p1;
|
||||
return true;
|
||||
}
|
||||
|
||||
oc2 = ss_oc(direction);
|
||||
|
||||
// Same side.
|
||||
if ((oc1 & oc2) > ClipNone)
|
||||
return false;
|
||||
|
||||
// Check intersections.
|
||||
if (oc1 & (ClipRight | ClipLeft))
|
||||
{
|
||||
if (oc1 & ClipRight)
|
||||
{
|
||||
if (n)
|
||||
n->Set(1, 0, 0);
|
||||
itr.x = mx.x;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (n)
|
||||
n->Set(-1, 0, 0);
|
||||
itr.x = mn.x;
|
||||
}
|
||||
float x1 = direction.x;
|
||||
float x2 = itr.x - p1.x;
|
||||
itr.y = p1.y + x2 * direction.y / x1;
|
||||
itr.z = p1.z + x2 * direction.z / x1;
|
||||
|
||||
if ((itr.y <= mx.y) && (itr.y >= mn.y) && (itr.z <= mx.z) && (itr.z >= mn.z))
|
||||
return true;
|
||||
}
|
||||
if (oc1 & (ClipTop | ClipBottom))
|
||||
{
|
||||
if (oc1 & ClipTop)
|
||||
{
|
||||
if (n)
|
||||
n->Set(0, 1, 0);
|
||||
itr.y = mx.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (n)
|
||||
n->Set(0, -1, 0);
|
||||
itr.y = mn.y;
|
||||
}
|
||||
float y1 = direction.y;
|
||||
float y2 = itr.y - p1.y;
|
||||
itr.x = p1.x + y2 * direction.x / y1;
|
||||
itr.z = p1.z + y2 * direction.z / y1;
|
||||
|
||||
if ((itr.x <= mx.x) && (itr.x >= mn.x) && (itr.z <= mx.z) && (itr.z >= mn.z))
|
||||
return true;
|
||||
}
|
||||
if (oc1 & (ClipFront | ClipBack))
|
||||
{
|
||||
if (oc1 & ClipBack)
|
||||
{
|
||||
if (n)
|
||||
n->Set(0, 0, 1);
|
||||
itr.z = mx.z;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (n)
|
||||
n->Set(0, 0, -1);
|
||||
itr.z = mn.z;
|
||||
}
|
||||
float z1 = direction.z;
|
||||
float z2 = itr.z - p1.z;
|
||||
itr.x = p1.x + z2 * direction.x / z1;
|
||||
itr.y = p1.y + z2 * direction.y / z1;
|
||||
|
||||
if ((itr.x <= mx.x) && (itr.x >= mn.x) && (itr.y <= mx.y) && (itr.y >= mn.y))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool MinMax::ClassifySegment(const Vector4 &p1, const Vector4 &p2, Vector4 &itr, Vector4 *n) const
|
||||
{
|
||||
uint oc1, oc2;
|
||||
|
||||
oc1 = cc_oc(mn, mx, p1);
|
||||
if (oc1 == ClipNone)
|
||||
{
|
||||
// Point inside bounding box.
|
||||
if (n)
|
||||
n->Set(0, 0, 0);
|
||||
itr = p1;
|
||||
return true;
|
||||
}
|
||||
|
||||
oc2 = cc_oc(mn, mx, p2);
|
||||
if (oc2 == ClipNone)
|
||||
{
|
||||
// point inside bounding box
|
||||
itr = p2;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Same side.
|
||||
if ((oc1 & oc2) > ClipNone)
|
||||
return false;
|
||||
|
||||
// Check intersections.
|
||||
if (oc1 & (ClipRight | ClipLeft))
|
||||
{
|
||||
if (oc1 & ClipRight)
|
||||
{
|
||||
if (n)
|
||||
n->Set(1, 0, 0);
|
||||
itr.x = mx.x;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (n)
|
||||
n->Set(-1, 0, 0);
|
||||
itr.x = mn.x;
|
||||
}
|
||||
|
||||
float x1 = p2.x - p1.x;
|
||||
float x2 = itr.x - p1.x;
|
||||
itr.y = p1.y + x2 * (p2.y - p1.y) / x1;
|
||||
itr.z = p1.z + x2 * (p2.z - p1.z) / x1;
|
||||
|
||||
if ( (itr.y <= mx.y) &&
|
||||
(itr.y >= mn.y) &&
|
||||
(itr.z <= mx.z) &&
|
||||
(itr.z >= mn.z) )
|
||||
return true;
|
||||
}
|
||||
if (oc1 & (ClipTop | ClipBottom))
|
||||
{
|
||||
if (oc1 & ClipTop)
|
||||
{
|
||||
if (n)
|
||||
n->Set(0, 1, 0);
|
||||
itr.y = mx.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (n)
|
||||
n->Set(0, -1, 0);
|
||||
itr.y = mn.y;
|
||||
}
|
||||
float y1 = p2.y - p1.y;
|
||||
float y2 = itr.y - p1.y;
|
||||
itr.x = p1.x + y2 * (p2.x - p1.x) / y1;
|
||||
itr.z = p1.z + y2 * (p2.z - p1.z) / y1;
|
||||
|
||||
if ( (itr.x <= mx.x) &&
|
||||
(itr.x >= mn.x) &&
|
||||
(itr.z <= mx.z) &&
|
||||
(itr.z >= mn.z) )
|
||||
return true;
|
||||
}
|
||||
if (oc1 & (ClipFront | ClipBack))
|
||||
{
|
||||
if (oc1 & ClipBack)
|
||||
{
|
||||
if (n)
|
||||
n->Set(0, 0, 1);
|
||||
itr.z = mx.z;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (n)
|
||||
n->Set(0, 0, -1);
|
||||
itr.z = mn.z;
|
||||
}
|
||||
float z1 = p2.z - p1.z;
|
||||
float z2 = itr.z - p1.z;
|
||||
itr.x = p1.x + z2 * (p2.x - p1.x) / z1;
|
||||
itr.y = p1.y + z2 * (p2.y - p1.y) / z1;
|
||||
|
||||
if ( (itr.x <= mx.x) &&
|
||||
(itr.x >= mn.x) &&
|
||||
(itr.y <= mx.y) &&
|
||||
(itr.y >= mn.y) )
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool MinMax::FromMetaTag(Tag &tag)
|
||||
{
|
||||
Tag *t;
|
||||
List <Tag *> ::Iterator i(tag.GetTags().GetRoot());
|
||||
|
||||
t = i.ObjectPtr();
|
||||
if (!t) return false;
|
||||
mn.FromMetaTag(*t);
|
||||
++i;
|
||||
|
||||
t = i.ObjectPtr();
|
||||
if (!t) return false;
|
||||
mx.FromMetaTag(*t);
|
||||
|
||||
return true;
|
||||
}
|
||||
Tag *MinMax::AsMetaTag()
|
||||
{
|
||||
Tag *root = new Tag("MinMax");
|
||||
if (root)
|
||||
{
|
||||
root->AddChild(mn.AsMetaTag("Min"));
|
||||
root->AddChild(mx.AsMetaTag("Max"));
|
||||
}
|
||||
return root;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
549
include/framework/geometry/curve.cpp
Normal file
549
include/framework/geometry/curve.cpp
Normal file
@ -0,0 +1,549 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include <cmath>
|
||||
#include "geometry/curve.h"
|
||||
#include "sort/sort.h"
|
||||
|
||||
using namespace GS;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Curve::Update(const CurvePoint &p, const Time &t_epsilon)
|
||||
{
|
||||
for (uint n = 0; n < points.GetCount(); ++n)
|
||||
{
|
||||
CurvePoint *point = points[n];
|
||||
if ((p.t >= (point->t - t_epsilon)) && (p.t <= (point->t + t_epsilon)))
|
||||
{
|
||||
point->v = p.v;
|
||||
return;
|
||||
}
|
||||
}
|
||||
Insert(p);
|
||||
}
|
||||
void Curve::Insert(const CurvePoint &k)
|
||||
{
|
||||
int idx = GetPointIndex(k.t);
|
||||
points.Insert(new CurvePoint(k), (idx == -1) ? points.GetCount() : idx);
|
||||
}
|
||||
void Curve::Append(const CurvePoint &k)
|
||||
{
|
||||
points.Insert(new CurvePoint(k), points.GetCount());
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Curve::Delete(CurvePoint *k)
|
||||
{
|
||||
points.Remove(k);
|
||||
delete k;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
int Curve::GetPointIndex(const Time &t, bool t_greater) const
|
||||
{
|
||||
if (points.GetCount() == 0)
|
||||
return -1;
|
||||
|
||||
if (t_greater)
|
||||
{
|
||||
for (uint n = 0; n < points.GetCount(); ++n)
|
||||
if (points[n]->t > t)
|
||||
return n;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int n = points.GetCount() - 1; n >= 0; --n)
|
||||
if (points[n]->t <= t)
|
||||
return n;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
TimeRange Curve::GetTimeRange() const
|
||||
{
|
||||
return points.GetCount() == 0 ? TimeRange() : TimeRange(points[0]->t, points[points.GetCount() - 1]->t);
|
||||
}
|
||||
Range <float> Curve::GetValueRange() const
|
||||
{
|
||||
if (points.GetCount() == 0)
|
||||
return Range <float> ();
|
||||
|
||||
Range <float> range(points[0]->v, points[0]->v);
|
||||
for (uint n = 1; n < points.GetCount(); ++n)
|
||||
{
|
||||
range.start = Types::Min(range.start, points[n]->v);
|
||||
range.end = Types::Max(range.end, points[n]->v);
|
||||
}
|
||||
return range;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
uint Curve::Optimize(uint point_count, const CurvePoint *skf, CurvePoint *dkf, float threshold)
|
||||
{
|
||||
if (point_count < 3)
|
||||
return 0;
|
||||
|
||||
uint ckf = 0, n;
|
||||
for (n = 1; n < (point_count - 1); n += 2)
|
||||
{
|
||||
float k = (skf[n].t - skf[n - 1].t).toSec() / (skf[n + 1].t - skf[n - 1].t).toSec();
|
||||
float iv = (skf[n - 1].v * k) + (skf[n + 1].v * (1.f - k));
|
||||
|
||||
dkf[ckf++] = skf[n - 1];
|
||||
if (fabs(skf[n].v - iv) > threshold)
|
||||
dkf[ckf++] = skf[n];
|
||||
}
|
||||
if (n == (point_count - 1))
|
||||
dkf[ckf++] = skf[point_count - 2];
|
||||
dkf[ckf++] = skf[point_count - 1];
|
||||
return point_count - ckf;
|
||||
}
|
||||
uint Curve::Optimize(float threshold)
|
||||
{
|
||||
if (!points.GetCount())
|
||||
return 0;
|
||||
|
||||
Array <CurvePoint> skf(points.GetCount(), Alloc::Curve), dkf(points.GetCount(), Alloc::Curve);
|
||||
if (skf.IsNull() || dkf.IsNull())
|
||||
__ERR__(__LOG__ << "Not enough memory.\n", 0);
|
||||
|
||||
// Freeze array.
|
||||
for (uint n = 0; n < points.GetCount(); ++n)
|
||||
skf[n] = *points[n];
|
||||
|
||||
// Optimize curve.
|
||||
uint gain = Optimize(points.GetCount(), skf.c_ptr(), dkf.c_ptr(), threshold), out = points.GetCount() - gain;
|
||||
|
||||
if (gain)
|
||||
{
|
||||
// Send back to curve.
|
||||
if (!AllocatePoint(out))
|
||||
__ERR__(__LOG__ << "Failed to reallocate optimized array.\n", 0);
|
||||
for (uint n = 0; n < points.GetCount(); ++n)
|
||||
SetPoint(n, dkf[n]);
|
||||
}
|
||||
return gain;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Curve::AllocatePoint(uint n)
|
||||
{
|
||||
ArrayListDeleteAllPtr(CurvePoint *, points)
|
||||
while (n--)
|
||||
if (!points.Add(new CurvePoint))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
void Curve::SetPoint(uint i, const CurvePoint &p) const
|
||||
{ *points[i] = p; }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Curve::Sort()
|
||||
{
|
||||
// Make sure there is work to do.
|
||||
bool need_sorting = false;
|
||||
for (uint n = 1; n < points.GetCount(); ++n)
|
||||
if (points[n - 1]->t > points[n]->t)
|
||||
{
|
||||
need_sorting = true;
|
||||
break;
|
||||
}
|
||||
if (!need_sorting)
|
||||
return;
|
||||
|
||||
// Sort keys.
|
||||
uint count = points.GetCount();
|
||||
|
||||
Array <GS::Sort<Time, CurvePoint *>::Entry> entries(count);
|
||||
for (uint n = 0; n < count; ++n)
|
||||
{
|
||||
entries[n].v = points[n]->t;
|
||||
entries[n].o = points[n];
|
||||
}
|
||||
GS::Sort<Time, CurvePoint *>::QuickSort(count, entries);
|
||||
|
||||
// Drop current array and rewrite ordered one.
|
||||
points.Clear(false);
|
||||
for (uint n = 0; n < count; ++n)
|
||||
points.Add(entries[n].o);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static float range(float v, float lo, float hi, int *i)
|
||||
{
|
||||
float r = hi - lo;
|
||||
|
||||
if (r == 0.f)
|
||||
{
|
||||
if (i)
|
||||
*i = 0;
|
||||
return lo;
|
||||
}
|
||||
|
||||
float v2 = v - lo;
|
||||
if (v2 >= 0.f)
|
||||
v2 = lo + v2 - r * floor(v2 / r);
|
||||
else
|
||||
v2 = hi + v2 - r * ceil(v2 / r);
|
||||
|
||||
if (i)
|
||||
*i = -(int)((v2 - v) / r + (v2 > v ? 0.5f : -0.5f));
|
||||
|
||||
return Types::Clamp(v2, lo, hi);
|
||||
}
|
||||
static void hermite(float t, float *h1, float *h2, float *h3, float *h4)
|
||||
{
|
||||
float t2 = t * t, t3 = t * t2;
|
||||
|
||||
*h2 = 3.f * t2 - t3 - t3;
|
||||
*h1 = 1.f - *h2;
|
||||
*h4 = t3 - t2;
|
||||
*h3 = *h4 - t2 + t;
|
||||
}
|
||||
static float bezier(float x0, float x1, float x2, float x3, float t)
|
||||
{
|
||||
float a, b, c, t2 = t * t, t3 = t * t2;
|
||||
|
||||
c = 3.f * (x1 - x0);
|
||||
b = 3.f * (x2 - x1) - c;
|
||||
a = x3 - x0 - c - b;
|
||||
|
||||
return a * t3 + b * t2 + c * t + x0;
|
||||
}
|
||||
static float bez2_time(float x0, float x1, float x2, float x3, float time, float *t0, float *t1)
|
||||
{
|
||||
float t = *t0 + (*t1 - *t0) * 0.5f, v = bezier(x0, x1, x2, x3, t);
|
||||
|
||||
if ((fabs(*t1 - *t0) > .0001f) && (fabs(time - v) > .0001f))
|
||||
{
|
||||
if (v > time)
|
||||
*t1 = t;
|
||||
else
|
||||
*t0 = t;
|
||||
|
||||
return bez2_time(x0, x1, x2, x3, time, t0, t1);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
static float bez2(const CurvePoint *key0, const CurvePoint *key1, float time)
|
||||
{
|
||||
float x, y, t, t0 = 0.f, t1 = 1.f;
|
||||
|
||||
if (key0->shape == CurvePoint::Shape_Bezier2)
|
||||
x = key0->t.toSec() + key0->param[2];
|
||||
else
|
||||
x = key0->t.toSec() + (key1->t - key0->t).toSec() / 3.f;
|
||||
|
||||
t = bez2_time(key0->t.toSec(), x, key1->t.toSec() + key1->param[0], key1->t.toSec(), time, &t0, &t1);
|
||||
|
||||
if (key0->shape == CurvePoint::Shape_Bezier2)
|
||||
y = key0->v + key0->param[3];
|
||||
else
|
||||
y = key0->v + key0->param[1] / 3.f;
|
||||
|
||||
return bezier(key0->v, y, key1->param[1] + key1->v, key1->v, t);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
float Curve::Outgoing(const CurvePoint *key0, const CurvePoint *key1, const CurvePoint *keyp) const
|
||||
{
|
||||
float a, b, d, t, out;
|
||||
|
||||
switch (key1->shape)
|
||||
{
|
||||
case CurvePoint::Shape_Linear:
|
||||
d = key1->v - key0->v;
|
||||
if (keyp)
|
||||
{
|
||||
t = (key1->t - key0->t).toSec() / (key1->t - keyp->t).toSec();
|
||||
out = t * ((key0->v - keyp->v) + d);
|
||||
}
|
||||
else
|
||||
out = d;
|
||||
break;
|
||||
|
||||
case CurvePoint::Shape_TCB:
|
||||
a = (1 - key0->tension)
|
||||
* (1 + key0->continuity)
|
||||
* (1 + key0->bias);
|
||||
b = (1 - key0->tension)
|
||||
* (1 - key0->continuity)
|
||||
* (1 - key0->bias);
|
||||
d = key1->v - key0->v;
|
||||
|
||||
if (keyp)
|
||||
{
|
||||
t = (key1->t - key0->t).toSec() / (key1->t - keyp->t).toSec();
|
||||
out = t * (a * (key0->v - keyp->v) + b * d);
|
||||
}
|
||||
else
|
||||
out = b * d;
|
||||
break;
|
||||
|
||||
case CurvePoint::Shape_Bezier:
|
||||
case CurvePoint::Shape_Hermite:
|
||||
out = key0->param[0];
|
||||
if (keyp)
|
||||
out *= (key1->t - key0->t).toSec() / (key1->t - keyp->t).toSec();
|
||||
break;
|
||||
|
||||
case CurvePoint::Shape_Bezier2:
|
||||
out = key0->param[3] * (key1->t - key0->t).toSec();
|
||||
if (fabs(key0->param[2]) > 1e-5f)
|
||||
out /= key0->param[2];
|
||||
else
|
||||
out *= 1e5f;
|
||||
break;
|
||||
|
||||
case CurvePoint::Shape_Step:
|
||||
default:
|
||||
out = 0;
|
||||
break;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
float Curve::Incoming(const CurvePoint *key0, const CurvePoint *key1, const CurvePoint *key2) const
|
||||
{
|
||||
float a, b, d, t, in;
|
||||
|
||||
switch (key1->shape)
|
||||
{
|
||||
case CurvePoint::Shape_Linear:
|
||||
d = key1->v - key0->v;
|
||||
if (key2)
|
||||
{
|
||||
t = (key1->t - key0->t).toSec() / (key2->t - key0->t).toSec();
|
||||
in = t * ((key2->v - key1->v) + d);
|
||||
}
|
||||
else
|
||||
in = d;
|
||||
break;
|
||||
|
||||
case CurvePoint::Shape_TCB:
|
||||
a = (1 - key1->tension)
|
||||
* (1 - key1->continuity)
|
||||
* (1 + key1->bias);
|
||||
b = (1 - key1->tension)
|
||||
* (1 + key1->continuity)
|
||||
* (1 - key1->bias);
|
||||
d = key1->v - key0->v;
|
||||
|
||||
if (key2)
|
||||
{
|
||||
t = (key1->t - key0->t).toSec() / (key2->t - key0->t).toSec();
|
||||
in = t * (b * (key2->v - key1->v) + a * d);
|
||||
}
|
||||
else
|
||||
in = a * d;
|
||||
break;
|
||||
|
||||
case CurvePoint::Shape_Bezier:
|
||||
case CurvePoint::Shape_Hermite:
|
||||
in = key1->param[0];
|
||||
if (key2)
|
||||
in *= (key1->t - key0->t).toSec() / (key2->t - key0->t).toSec();
|
||||
break;
|
||||
|
||||
case CurvePoint::Shape_Bezier2:
|
||||
in = key1->param[1] * (key1->t - key0->t).toSec();
|
||||
if (fabs(key1->param[0]) > 1e-5f)
|
||||
in /= key1->param[0];
|
||||
else in *= 1e5f;
|
||||
break;
|
||||
|
||||
case CurvePoint::Shape_Step:
|
||||
default:
|
||||
in = 0;
|
||||
break;
|
||||
}
|
||||
return in;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Curve::Evaluate(Time t, float *p, LoopMode loop, Time loop_start, Time loop_end) const
|
||||
{
|
||||
int point_count = points.GetCount();
|
||||
|
||||
if (point_count == 0)
|
||||
{
|
||||
*p = 0;
|
||||
return;
|
||||
}
|
||||
if (point_count == 1)
|
||||
{
|
||||
*p = points[0]->v;
|
||||
return;
|
||||
}
|
||||
|
||||
// Loop mode.
|
||||
CurvePoint *skey = points[0], *ekey = points[point_count - 1];
|
||||
|
||||
loop_start = (loop_start == Time::Inf) ? skey->t : Types::Clamp(loop_start, skey->t, ekey->t);
|
||||
loop_end = (loop_end == Time::Inf) ? ekey->t : Types::Clamp(loop_end, skey->t, ekey->t);
|
||||
|
||||
int noff = 0;
|
||||
float offset = 0;
|
||||
if (t < loop_start)
|
||||
{
|
||||
switch (loop)
|
||||
{
|
||||
case Reset:
|
||||
*p = 0.f;
|
||||
return;
|
||||
|
||||
default:
|
||||
case Constant:
|
||||
Evaluate(loop_start, p, loop, loop_start, loop_end);
|
||||
return;
|
||||
case Repeat:
|
||||
t.setSec(range(t.toSec(), loop_start.toSec(), loop_end.toSec(), NULL));
|
||||
break;
|
||||
case Oscillate:
|
||||
t.setSec(range(t.toSec(), loop_start.toSec(), loop_end.toSec(), &noff));
|
||||
if (noff % 2)
|
||||
t = loop_end + loop_start - t;
|
||||
break;
|
||||
case OffsetAndRepeat:
|
||||
t.setSec(range(t.toSec(), loop_start.toSec(), loop_end.toSec(), &noff));
|
||||
offset = noff * (ekey->v - skey->v); // Broken on custom loop point.
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (t > loop_end)
|
||||
{
|
||||
switch (loop)
|
||||
{
|
||||
case Reset:
|
||||
*p = 0.f;
|
||||
return;
|
||||
|
||||
default:
|
||||
case Constant:
|
||||
Evaluate(loop_end, p, loop, loop_start, loop_end);
|
||||
return;
|
||||
case Repeat:
|
||||
t.setSec(range(t.toSec(), loop_start.toSec(), loop_end.toSec(), NULL));
|
||||
break;
|
||||
case Oscillate:
|
||||
t.setSec(range(t.toSec(), loop_start.toSec(), loop_end.toSec(), &noff));
|
||||
if (noff % 2)
|
||||
t = loop_end + loop_start - t;
|
||||
break;
|
||||
case OffsetAndRepeat:
|
||||
t.setSec(range(t.toSec(), loop_start.toSec(), loop_end.toSec(), &noff));
|
||||
offset = noff * (ekey->v - skey->v);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Seek to current key.
|
||||
int ikey0;
|
||||
#if 1
|
||||
{
|
||||
uint lo = 0, hi = points.GetCount() - 1;
|
||||
|
||||
forever
|
||||
{
|
||||
uint mid = (lo + hi) / 2;
|
||||
|
||||
if (points[mid]->t > t)
|
||||
hi = mid;
|
||||
else
|
||||
{
|
||||
if (lo == mid)
|
||||
{
|
||||
ikey0 = lo;
|
||||
break;
|
||||
}
|
||||
else
|
||||
lo = mid;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
ikey0 = 0;
|
||||
while (((ikey0 + 1) < point_count) && (t > points[ikey0 + 1]->t))
|
||||
ikey0++;
|
||||
#endif
|
||||
|
||||
CurvePoint *pkey0 = points[ikey0];
|
||||
|
||||
if (pkey0 == NULL)
|
||||
return;
|
||||
|
||||
// Sample curve.
|
||||
CurvePoint *pkeyp = ikey0 > 0 ? points[ikey0 - 1] : NULL;
|
||||
|
||||
int ikey1 = ikey0 + 1;
|
||||
CurvePoint *pkey1 = points[ikey1], *pkey2 = ikey1 < (point_count - 1) ? points[ikey1 + 1] : NULL;
|
||||
|
||||
if (t == pkey0->t)
|
||||
*p = pkey0->v + offset;
|
||||
|
||||
else if (t == pkey1->t)
|
||||
*p = pkey1->v + offset;
|
||||
|
||||
else
|
||||
{
|
||||
const float k_t = (t - pkey0->t).toSec() / (pkey1->t - pkey0->t).toSec();
|
||||
|
||||
switch (pkey0->shape)
|
||||
{
|
||||
case CurvePoint::Shape_TCB:
|
||||
case CurvePoint::Shape_Bezier:
|
||||
case CurvePoint::Shape_Hermite:
|
||||
{
|
||||
float out = Outgoing(pkey0, pkey1, pkeyp), in = Incoming(pkey0, pkey1, pkey2);
|
||||
|
||||
float h1, h2, h3, h4;
|
||||
hermite(k_t, &h1, &h2, &h3, &h4);
|
||||
*p = h1 * pkey0->v + h2 * pkey1->v + h3 * out + h4 * in + offset;
|
||||
}
|
||||
break;
|
||||
|
||||
case CurvePoint::Shape_Bezier2:
|
||||
*p = bez2(pkey0, pkey1, k_t) + offset;
|
||||
break;
|
||||
|
||||
case CurvePoint::Shape_Linear:
|
||||
*p = pkey0->v + k_t * (pkey1->v - pkey0->v) + offset;
|
||||
break;
|
||||
|
||||
case CurvePoint::Shape_Step:
|
||||
*p = pkey0->v + offset;
|
||||
break;
|
||||
|
||||
default:
|
||||
*p = offset;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Curve::Clear()
|
||||
{
|
||||
ArrayListDeleteAllPtr(CurvePoint *, points)
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Curve::~Curve()
|
||||
{ Clear(); }
|
||||
//------------------------------------------------------------------------------
|
||||
268
include/framework/geometry/curve_nml.cpp
Normal file
268
include/framework/geometry/curve_nml.cpp
Normal file
@ -0,0 +1,268 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include "geometry/curve.h"
|
||||
#include "math/nmath.h"
|
||||
#include "sort/sort.h"
|
||||
#include "alloc/ialloc.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::NML;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Reflection::Enum::Dict Curve::loop_mode_dict[] =
|
||||
{
|
||||
{ Curve::Reset, "Reset" },
|
||||
{ Curve::Constant, "Constant" },
|
||||
{ Curve::Repeat, "Repeat" },
|
||||
{ Curve::Oscillate, "Oscillate" },
|
||||
{ Curve::OffsetAndRepeat, "OffsetAndRepeat" },
|
||||
{ 0, 0 }
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// ARM odd address read/write helper functions.
|
||||
void ARM_unaligned_read(float &out, const char *addr)
|
||||
{
|
||||
char *p_out = (char *)&out;
|
||||
for (int n = 0; n < sizeof(float); ++n)
|
||||
p_out[n] = addr[n];
|
||||
}
|
||||
void ARM_unaligned_write(char *addr, const float &in)
|
||||
{
|
||||
const char *p_in = (const char *)∈
|
||||
for (int n = 0; n < sizeof(float); ++n)
|
||||
addr[n] = p_in[n];
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Curve::FromMetaTag(Tag &tag)
|
||||
{
|
||||
if (tag.name != "Curve")
|
||||
__ERR__(__LOG_E__ << "Could not parse curve, incorrect root tag (" << tag.name << ").\n", false)
|
||||
|
||||
Clear();
|
||||
|
||||
// Parse root tags.
|
||||
NMLTagForeach(pt, tag)
|
||||
{
|
||||
if (pt->name == "BinaryKnot")
|
||||
{
|
||||
Tag *count_tag = pt->GetTag("Count"), *data_tag = pt->GetTag("Data");
|
||||
|
||||
if (count_tag && data_tag)
|
||||
{
|
||||
char *data = (char *)data_tag->GetValue().GetBinaryBuffer(), *p_data = data;
|
||||
|
||||
if (data && AllocatePoint(count_tag->GetInteger()))
|
||||
for (uint n = 0; n < points.GetCount(); ++n)
|
||||
{
|
||||
CurvePoint *p = points[n];
|
||||
|
||||
p->shape = CurvePoint::Shape(*p_data++);
|
||||
|
||||
float t;
|
||||
ARM_unaligned_read(t, p_data + 0);
|
||||
p->t.setSec(t);
|
||||
ARM_unaligned_read(p->v, p_data + 4);
|
||||
|
||||
if (p->shape == CurvePoint::Shape_Linear)
|
||||
p_data += 2 * 4;
|
||||
|
||||
else
|
||||
{
|
||||
ARM_unaligned_read(p->tension, p_data + 8);
|
||||
ARM_unaligned_read(p->continuity, p_data + 12);
|
||||
ARM_unaligned_read(p->bias, p_data + 16);
|
||||
|
||||
for (int n = 0; n < 4; ++n)
|
||||
ARM_unaligned_read(p->param[n], p_data + 20 + n * 4);
|
||||
|
||||
p_data += 9 * 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (pt->name == "Knot")
|
||||
{
|
||||
Tag *st = pt->GetTags()[0];
|
||||
if (!st || (st->name != "Count"))
|
||||
__ERR__(__LOG_E__ << "First sub-tag in <Curve> must be the knot <Count> tag.\n", false)
|
||||
|
||||
if (!AllocatePoint((uint)st->GetInteger()))
|
||||
return false;
|
||||
|
||||
static String _count("Count"), _knot("Knot"), _knotex("KnotEx");
|
||||
|
||||
uint n = 0;
|
||||
NMLTagForeach(st, *pt)
|
||||
{
|
||||
if (st->name == _count)
|
||||
{}
|
||||
|
||||
// Legacy knot definition.
|
||||
if (st->name == _knot)
|
||||
{
|
||||
if (n == points.GetCount())
|
||||
{
|
||||
__LOG_E__ << "Too many knot in <Curve>, " << points.GetCount() << " expected.\n";
|
||||
break;
|
||||
}
|
||||
|
||||
if (const char *p = st->GetString())
|
||||
{
|
||||
points[n]->t = Time::fromSec(String::atof(p));
|
||||
points[n]->shape = CurvePoint::Shape_Linear;
|
||||
|
||||
p = String::strfindchar(p, ':');
|
||||
points[n]->v = p[0] ? String::atof(p + 1) : 0;
|
||||
|
||||
n++;
|
||||
}
|
||||
else
|
||||
__LOG_W__ << "Invalid knot tag while parsing curve.\n";
|
||||
}
|
||||
|
||||
/*
|
||||
Extended knot definition.
|
||||
*/
|
||||
else if (st->name == _knotex)
|
||||
{
|
||||
if (n == points.GetCount())
|
||||
{
|
||||
__LOG_E__ << "Too many knot in <Knot>, " << points.GetCount() << " specified.\n";
|
||||
break;
|
||||
}
|
||||
|
||||
if (const char *p = st->GetString())
|
||||
{
|
||||
CurvePoint *_knot = points[n];
|
||||
_knot->t = Time::fromSec(String::atof(p));
|
||||
|
||||
// Read shape.
|
||||
p = String::strfindchar(p, ':');
|
||||
int shape = p[0] ? String::atoi(p + 1) : 0;
|
||||
p++;
|
||||
|
||||
switch (shape)
|
||||
{
|
||||
default:
|
||||
case 0: _knot->shape = CurvePoint::Shape_None; break;
|
||||
case 1: _knot->shape = CurvePoint::Shape_Linear; break;
|
||||
case 2: _knot->shape = CurvePoint::Shape_Bezier; break;
|
||||
case 3: _knot->shape = CurvePoint::Shape_Bezier2; break;
|
||||
case 4: _knot->shape = CurvePoint::Shape_Hermite; break;
|
||||
case 5: _knot->shape = CurvePoint::Shape_TCB; break;
|
||||
case 6: _knot->shape = CurvePoint::Shape_Step; break;
|
||||
}
|
||||
|
||||
// Read knot parameters.
|
||||
//--------------------------------------------
|
||||
#define GetInputKnotParamEx(_PARM_)\
|
||||
{\
|
||||
p = String::strfindchar(p, ':');\
|
||||
(_PARM_) = p[0] ? String::atof(p + 1) : -1;\
|
||||
p++;\
|
||||
}
|
||||
//--------------------------------------------
|
||||
|
||||
GetInputKnotParamEx(_knot->v);
|
||||
GetInputKnotParamEx(_knot->tension);
|
||||
GetInputKnotParamEx(_knot->continuity);
|
||||
GetInputKnotParamEx(_knot->bias);
|
||||
|
||||
GetInputKnotParamEx(_knot->param[0]);
|
||||
GetInputKnotParamEx(_knot->param[1]);
|
||||
GetInputKnotParamEx(_knot->param[2]);
|
||||
GetInputKnotParamEx(_knot->param[3]);
|
||||
n++;
|
||||
}
|
||||
else
|
||||
__LOG_W__ << "Invalid extended knot tag while parsing curve.\n";
|
||||
}
|
||||
else
|
||||
__LOG_W__ << "Unsupported knot tag '" << st->name << "'.\n";
|
||||
}
|
||||
|
||||
// Incomplete/erroneous definition.
|
||||
if (n != points.GetCount())
|
||||
{
|
||||
Clear();
|
||||
__ERR__(__LOG_E__ << "<Curve> is corrupted, discarding.\n", false)
|
||||
}
|
||||
}
|
||||
else __LOG_W__ << "Unknown tag '" << pt->name << "' in <Curve>.\n";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Tag *Curve::AsMetaTag() const
|
||||
{
|
||||
Tag *root = new Tag("Curve");
|
||||
if (!root)
|
||||
__ERR__(__LOG_E__ << "Could not create curve root tag to serialize.\n", NULL)
|
||||
|
||||
// Binary knots.
|
||||
if (points.GetCount())
|
||||
if (Tag *binary_knot_tag = root->AddChild("BinaryKnot"))
|
||||
{
|
||||
binary_knot_tag->AddChild("Count", points.GetCount());
|
||||
|
||||
// Get size.
|
||||
int size = 0;
|
||||
for (uint n = 0; n < points.GetCount(); ++n)
|
||||
{
|
||||
CurvePoint *_knot = points[n];
|
||||
|
||||
// Legacy definition.
|
||||
if (_knot->shape == CurvePoint::Shape_Linear)
|
||||
size += 2 * 4; // Knot size.
|
||||
else size += 9 * 4; // Extended knot size.
|
||||
}
|
||||
|
||||
// Output binary.
|
||||
Array <char> knot_array(points.GetCount() + size);
|
||||
char *p_knot = knot_array;
|
||||
|
||||
for (uint n = 0; n < points.GetCount(); ++n)
|
||||
{
|
||||
CurvePoint *_knot = points[n];
|
||||
|
||||
*p_knot++ = uchar(_knot->shape);
|
||||
|
||||
float t = _knot->t.toSec();
|
||||
ARM_unaligned_write(p_knot + 0, t);
|
||||
ARM_unaligned_write(p_knot + 4, _knot->v);
|
||||
|
||||
if (_knot->shape == CurvePoint::Shape_Linear)
|
||||
p_knot += 2 * 4;
|
||||
|
||||
else
|
||||
{
|
||||
ARM_unaligned_write(p_knot + 8, _knot->tension);
|
||||
ARM_unaligned_write(p_knot + 12, _knot->continuity);
|
||||
ARM_unaligned_write(p_knot + 16, _knot->bias);
|
||||
|
||||
for (int n = 0; n < 4; ++n)
|
||||
ARM_unaligned_write(p_knot + 20 + n * 4, _knot->param[n]);
|
||||
|
||||
p_knot += 9 * 4;
|
||||
}
|
||||
}
|
||||
|
||||
binary_knot_tag->AddChild("Data", knot_array, points.GetCount() + size);
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
309
include/framework/geometry/frustum.cpp
Normal file
309
include/framework/geometry/frustum.cpp
Normal file
@ -0,0 +1,309 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include <cmath>
|
||||
#include "geometry/frustum.h"
|
||||
#include "geometry/bounding_box.h"
|
||||
#include "geometry/sat.h"
|
||||
#include "shape/shape.h"
|
||||
#include "math/matrix4.h"
|
||||
|
||||
using namespace GS;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Frustum::SetPerspective(float fov, float znear, float zfar, const Matrix4 *matrix, float h_ar, float v_ar)
|
||||
{
|
||||
fov *= 0.5f;
|
||||
const float hyp = tan(fov);
|
||||
const float hfov = atan(hyp / h_ar), vfov = atan(hyp / v_ar);
|
||||
|
||||
Vector4 n;
|
||||
|
||||
const float sinv = sin(vfov);
|
||||
const float cosv = cos(vfov);
|
||||
n.Set(0, cosv, -sinv);
|
||||
plane[Top].Set(NULL, n, matrix);
|
||||
n.Set(0, -cosv, -sinv);
|
||||
plane[Bottom].Set(NULL, n, matrix);
|
||||
|
||||
const float sinh = sin(hfov);
|
||||
const float cosh = cos(hfov);
|
||||
n.Set(-cosh, 0, -sinh);
|
||||
plane[Left].Set(NULL, n, matrix);
|
||||
n.Set(cosh, 0, -sinh);
|
||||
plane[Right].Set(NULL, n, matrix);
|
||||
|
||||
Vector4 s;
|
||||
s.Set(0, 0, znear);
|
||||
n.Set(0, 0, -1);
|
||||
plane[Near].Set(&s, n, matrix);
|
||||
s.Set(0, 0, zfar);
|
||||
n.Set(0, 0, 1);
|
||||
plane[Far].Set(&s, n, matrix);
|
||||
|
||||
// Model vertices.
|
||||
Vector4 bvtx[8];
|
||||
Vector4 *_vtx = matrix ? bvtx : vtx;
|
||||
|
||||
// Compute near plane corners.
|
||||
float k = znear / -cosv;
|
||||
_vtx[0].y = -sinv * k;
|
||||
_vtx[0].z = znear;//-cosv * k;
|
||||
k = znear / cosh;
|
||||
_vtx[0].x = -sinh * k;
|
||||
_vtx[0].w = 1;
|
||||
|
||||
_vtx[1].Set(-_vtx[0].x, _vtx[0].y, _vtx[0].z);
|
||||
_vtx[2].Set(-_vtx[0].x, -_vtx[0].y, _vtx[0].z);
|
||||
_vtx[3].Set(_vtx[0].x, -_vtx[0].y, _vtx[0].z);
|
||||
|
||||
// Compute far plane corners.
|
||||
k = zfar / -cosv;
|
||||
_vtx[4].y = -sinv * k;
|
||||
_vtx[4].z = zfar;//-cosv * k;
|
||||
k = zfar / cosh;
|
||||
_vtx[4].x = -sinh * k;
|
||||
_vtx[4].w = 1;
|
||||
|
||||
_vtx[5].Set(-_vtx[4].x, _vtx[4].y, _vtx[4].z);
|
||||
_vtx[6].Set(-_vtx[4].x, -_vtx[4].y, _vtx[4].z);
|
||||
_vtx[7].Set(_vtx[4].x, -_vtx[4].y, _vtx[4].z);
|
||||
|
||||
if (matrix)
|
||||
matrix->Apply(vtx, bvtx, 8);
|
||||
}
|
||||
void Frustum::SetOrthographic(float width, float height, float znear, float zfar, const Matrix4 *matrix, float h_ar, float v_ar)
|
||||
{
|
||||
Vector4 s, n;
|
||||
|
||||
width *= h_ar;
|
||||
height *= v_ar;
|
||||
|
||||
s.Set(0, height * 0.5f, 0);
|
||||
n.Set(0, 1, 0);
|
||||
plane[Top].Set(&s, n, matrix);
|
||||
s.Set(0, -height * 0.5f, 0);
|
||||
n.Set(0, -1, 0);
|
||||
plane[Bottom].Set(&s, n, matrix);
|
||||
s.Set(-width * 0.5f, 0, 0);
|
||||
n.Set(-1, 0, 0);
|
||||
plane[Left].Set(&s, n, matrix);
|
||||
s.Set(width * 0.5f, 0, 0);
|
||||
n.Set(1, 0, 0);
|
||||
plane[Right].Set(&s, n, matrix);
|
||||
s.Set(0, 0, znear);
|
||||
n.Set(0, 0, -1);
|
||||
plane[Near].Set(&s, n, matrix);
|
||||
s.Set(0, 0, zfar);
|
||||
n.Set(0, 0, 1);
|
||||
plane[Far].Set(&s, n, matrix);
|
||||
|
||||
// Model vertices.
|
||||
Vector4 bvtx[8];
|
||||
Vector4 *_vtx = matrix ? bvtx : vtx;
|
||||
|
||||
// Compute near plane corners.
|
||||
_vtx[0].Set(-width * 0.5f, height * 0.5f, znear);
|
||||
_vtx[1].Set( width * 0.5f, height * 0.5f, znear);
|
||||
_vtx[2].Set( width * 0.5f, -height * 0.5f, znear);
|
||||
_vtx[3].Set(-width * 0.5f, -height * 0.5f, znear);
|
||||
_vtx[4].Set(-width * 0.5f, height * 0.5f, zfar);
|
||||
_vtx[5].Set( width * 0.5f, height * 0.5f, zfar);
|
||||
_vtx[6].Set( width * 0.5f, -height * 0.5f, zfar);
|
||||
_vtx[7].Set(-width * 0.5f, -height * 0.5f, zfar);
|
||||
|
||||
if (matrix)
|
||||
matrix->Apply(vtx, bvtx, 8);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Frustum::Visibility Frustum::ClassifyShape(const Shape &s, const Matrix4 *m) const
|
||||
{
|
||||
Visibility v = Inside;
|
||||
|
||||
Matrix3 rm;
|
||||
if (m)
|
||||
rm = Matrix3::FromMatrix4(*m).Transposed();
|
||||
|
||||
for (uint n = 0; n < 6; ++n)
|
||||
{
|
||||
float d, r;
|
||||
|
||||
if (m)
|
||||
{
|
||||
d = plane[n].DistanceToPlane(s.GetCenter() * m[0]);
|
||||
r = s.GetSupportDistance(plane[n].GetNormal() * rm);
|
||||
}
|
||||
else
|
||||
{
|
||||
d = plane[n].DistanceToPlane(s.GetCenter());
|
||||
r = s.GetSupportDistance(plane[n].GetNormal());
|
||||
}
|
||||
|
||||
if (d > r)
|
||||
return Outside;
|
||||
if (d > -r)
|
||||
v = Clipped;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
Frustum::Visibility Frustum::ClassifySphere(const Vector4 &p, float r) const
|
||||
{
|
||||
Visibility v = Inside;
|
||||
for (uint n = 0; n < 6; ++n)
|
||||
{
|
||||
if (plane[n].DistanceToPlane(p) > r)
|
||||
return Outside;
|
||||
if (plane[n].DistanceToPlane(p) > -r)
|
||||
v = Clipped;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
Frustum::Visibility Frustum::ClassifySet(uint count, const Vector4 * const GSRESTRICT set, const float offset) const
|
||||
{
|
||||
Visibility v = Inside;
|
||||
for (uint n = 0; n < 6; ++n)
|
||||
{
|
||||
uint out = 0;
|
||||
for (uint i = 0; i < count; ++i)
|
||||
if (plane[n].DistanceToPlane(set[i]) > offset)
|
||||
++out;
|
||||
|
||||
if (out == count)
|
||||
return Outside;
|
||||
if (out > 0)
|
||||
v = Clipped;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if (__PLATFORM_NINTENDO_WII__ == 0)
|
||||
//#define FRUSTUM_TEST_USE_SAT
|
||||
#endif
|
||||
|
||||
//--------------------------------------------
|
||||
#define SAT_TEST(_N_, _U_, _A_, _V_, _B_)\
|
||||
{\
|
||||
SAT::Overlap _v = SAT::TestOverlap(_N_, _U_, _A_, _V_, _B_);\
|
||||
if (_v == SAT::Outside)\
|
||||
return Outside;\
|
||||
if (_v == SAT::Clipped)\
|
||||
v = Clipped;\
|
||||
}
|
||||
//--------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Frustum::Visibility Frustum::ClassifyMinMax(const MinMax &mm, const Matrix4 *matrix) const
|
||||
{
|
||||
// TODO Please, use AABB half width and implicit interval projection... will you?
|
||||
Vector4 s[8], d[8], *p;
|
||||
|
||||
s[0].Set(mm.mn.x, mm.mn.y, mm.mn.z);
|
||||
s[1].Set(mm.mx.x, mm.mn.y, mm.mn.z);
|
||||
s[2].Set(mm.mx.x, mm.mx.y, mm.mn.z);
|
||||
s[3].Set(mm.mn.x, mm.mx.y, mm.mn.z);
|
||||
s[4].Set(mm.mn.x, mm.mn.y, mm.mx.z);
|
||||
s[5].Set(mm.mx.x, mm.mn.y, mm.mx.z);
|
||||
s[6].Set(mm.mx.x, mm.mx.y, mm.mx.z);
|
||||
s[7].Set(mm.mn.x, mm.mx.y, mm.mx.z);
|
||||
|
||||
if (matrix)
|
||||
{
|
||||
matrix->Apply(d, s, 8);
|
||||
p = d;
|
||||
}
|
||||
else
|
||||
p = s;
|
||||
|
||||
#ifndef FRUSTUM_TEST_USE_SAT
|
||||
// Faster but much coarser test.
|
||||
return ClassifySet(8, p);
|
||||
#else
|
||||
// Frustum/AABB SAT.
|
||||
Visibility v = Inside;
|
||||
|
||||
// Test face/{face/edge} contact.
|
||||
SAT_TEST(plane[Top].GetNormal(), 8, vtx, 8, p);
|
||||
SAT_TEST(plane[Bottom].GetNormal(), 8, vtx, 8, p);
|
||||
SAT_TEST(plane[Left].GetNormal(), 8, vtx, 8, p);
|
||||
SAT_TEST(plane[Right].GetNormal(), 8, vtx, 8, p);
|
||||
SAT_TEST(plane[Near].GetNormal(), 8, vtx, 8, p);
|
||||
SAT_TEST(plane[Far].GetNormal(), 8, vtx, 8, p);
|
||||
|
||||
Vector4 _edge[3];
|
||||
|
||||
_edge[0] = matrix ? matrix->GetRow(0) : Vector4(1, 0, 0);
|
||||
SAT_TEST(_edge[0], 8, vtx, 8, p);
|
||||
_edge[1] = matrix ? matrix->GetRow(1) : Vector4(0, 1, 0);
|
||||
SAT_TEST(_edge[1], 8, vtx, 8, p);
|
||||
_edge[2] = matrix ? matrix->GetRow(2) : Vector4(0, 0, 1);
|
||||
SAT_TEST(_edge[2], 8, vtx, 8, p);
|
||||
|
||||
// Test edge/edge contact.
|
||||
Vector4 edge[6];
|
||||
for (uint n = 0; n < 4; ++n)
|
||||
edge[n] = vtx[n + 4] - vtx[n];
|
||||
edge[4] = vtx[1] - vtx[0];
|
||||
edge[5] = vtx[3] - vtx[0];
|
||||
|
||||
for (uint n = 0; n < 6; ++n)
|
||||
for (uint m = 0; m < 3; ++m)
|
||||
{
|
||||
Vector4 axis = edge[n].Cross(_edge[m]);
|
||||
if (Math::EqualZero(axis.Len2()))
|
||||
continue;
|
||||
SAT_TEST(axis, 8, vtx, 8, p);
|
||||
}
|
||||
return v;
|
||||
#endif
|
||||
}
|
||||
Frustum::Visibility Frustum::ClassifyFrustrum(const Frustum &frustum) const
|
||||
{
|
||||
#ifndef FRUSTUM_TEST_USE_SAT
|
||||
// Faster but much coarser test.
|
||||
return ClassifySet(8, frustum.vtx);
|
||||
#else
|
||||
// Frustum/frustum SAT.
|
||||
Visibility v = Inside;
|
||||
|
||||
// Test face/{face/edge} contact.
|
||||
SAT_TEST(plane[Top].GetNormal(), 8, vtx, 8, frustum.vtx);
|
||||
SAT_TEST(plane[Bottom].GetNormal(), 8, vtx, 8, frustum.vtx);
|
||||
SAT_TEST(plane[Left].GetNormal(), 8, vtx, 8, frustum.vtx);
|
||||
SAT_TEST(plane[Right].GetNormal(), 8, vtx, 8, frustum.vtx);
|
||||
SAT_TEST(plane[Far].GetNormal(), 8, vtx, 8, frustum.vtx);
|
||||
SAT_TEST(frustum.plane[Top].GetNormal(), 8, vtx, 8, frustum.vtx);
|
||||
SAT_TEST(frustum.plane[Bottom].GetNormal(), 8, vtx, 8, frustum.vtx);
|
||||
SAT_TEST(frustum.plane[Left].GetNormal(), 8, vtx, 8, frustum.vtx);
|
||||
SAT_TEST(frustum.plane[Right].GetNormal(), 8, vtx, 8, frustum.vtx);
|
||||
SAT_TEST(frustum.plane[Far].GetNormal(), 8, vtx, 8, frustum.vtx);
|
||||
|
||||
// Test edge/edge contact.
|
||||
Vector4 edge[6], _edge[6];
|
||||
for (uint n = 0; n < 4; ++n)
|
||||
{
|
||||
edge[n] = vtx[n + 4] - vtx[n];
|
||||
_edge[n] = frustum.vtx[n + 4] - frustum.vtx[n];
|
||||
}
|
||||
edge[4] = vtx[1] - vtx[0];
|
||||
edge[5] = vtx[3] - vtx[0];
|
||||
_edge[4] = frustum.vtx[1] - frustum.vtx[0];
|
||||
_edge[5] = frustum.vtx[3] - frustum.vtx[0];
|
||||
|
||||
for (uint n = 0; n < 6; ++n)
|
||||
for (uint m = 0; m < 6; ++m)
|
||||
{
|
||||
Vector4 axis = edge[n].Cross(_edge[m]);
|
||||
if (!Math::EqualZero(axis.Len2()))
|
||||
SAT_TEST(axis, 8, vtx, 8, frustum.vtx);
|
||||
}
|
||||
return v;
|
||||
#endif
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
122
include/framework/geometry/geometric_tools.cpp
Normal file
122
include/framework/geometry/geometric_tools.cpp
Normal file
@ -0,0 +1,122 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include <cmath>
|
||||
#include "geometry/geometric_tools.h"
|
||||
|
||||
|
||||
namespace GS {
|
||||
namespace Geometric {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
float TriArea2D(float x0, float y0, float x1, float y1, float x2, float y2)
|
||||
{ return (x0 - x1) * (y1 - y2) - (x1 - x2) * (y0 - y1); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Barycentric(const Vector4 &a, const Vector4 &b, const Vector4 &c, const Vector4 &p, float &u, float &v, float &w)
|
||||
{
|
||||
Vector4 m = (b - a).Cross(c - a);
|
||||
|
||||
float nu, nv, ood;
|
||||
float x = fabs(m.x), y = fabs(m.y), z = fabs(m.z);
|
||||
|
||||
if (x >= y && x >= z)
|
||||
{
|
||||
nu = TriArea2D(p.y, p.z, b.y, b.z, c.y, c.z);
|
||||
nv = TriArea2D(p.y, p.z, c.y, c.z, a.y, a.z);
|
||||
ood = 1.f / m.x;
|
||||
}
|
||||
else if (y >= x && y >= z)
|
||||
{
|
||||
nu = TriArea2D(p.x, p.z, b.x, b.z, c.x, c.z);
|
||||
nv = TriArea2D(p.x, p.z, c.x, c.z, a.x, a.z);
|
||||
ood = 1.f / -m.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
nu = TriArea2D(p.x, p.y, b.x, b.y, c.x, c.y);
|
||||
nv = TriArea2D(p.x, p.y, c.x, c.y, a.x, a.y);
|
||||
ood = 1.f / m.z;
|
||||
}
|
||||
u = nu * ood;
|
||||
v = nv * ood;
|
||||
w = 1.f - u - v;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool LineIntersectPlane(const Vector4 &a, const Vector4 &v, const Vector4 &n, const Vector4 &p, float &t)
|
||||
{
|
||||
float k = v.Dot(n);
|
||||
if (Math::EqualZero(k))
|
||||
return false;
|
||||
t = (p.Dot(n) - a.Dot(n)) / k;
|
||||
return true;
|
||||
}
|
||||
bool LineIntersectSphere(const Vector4 &a, const Vector4 &v, const Vector4 &c, float r, float t[2])
|
||||
{
|
||||
Vector4 e = c - a;
|
||||
|
||||
float k = e.Dot(v);
|
||||
float d = r * r - (e.Len2() - k * k);
|
||||
if (d < 0)
|
||||
return false;
|
||||
|
||||
d = Math::Sqrt(d);
|
||||
|
||||
if (t)
|
||||
{
|
||||
t[0] = k - d;
|
||||
t[1] = k + d;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
float LineClosestPoint(const Vector4 &a, const Vector4 &b, const Vector4 &u, Vector4 *p)
|
||||
{
|
||||
Vector4 _u = u - a;
|
||||
Vector4 _v = b - a;
|
||||
|
||||
float t = _u.Dot(_v) / _v.Dot(_v);
|
||||
|
||||
if (p)
|
||||
p[0] = _v * t + a;
|
||||
|
||||
return t;
|
||||
}
|
||||
bool LineClosestPointToLine(const Vector4 &a, const Vector4 &b, const Vector4 &la, const Vector4 &lb, float t[2])
|
||||
{
|
||||
Vector4 u = b - a, v = lb - la;
|
||||
float ul2 = u.Len2(), vl2 = v.Len2();
|
||||
|
||||
float d = u.Dot(v), k = ul2 * vl2 - d * d;
|
||||
|
||||
if (fabs(k) < 0.00000001f)
|
||||
return false;
|
||||
|
||||
k = 1.f / k;
|
||||
float uv = d, du = (la - a).Dot(u), dv = (a - la).Dot(v);
|
||||
|
||||
t[0] = (vl2 * du + uv * dv) * k;
|
||||
t[1] = (uv * du + ul2 * dv) * k;
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
float SegmentClosestPoint(const Vector4 &a, const Vector4 &b, const Vector4 &u, Vector4 *p)
|
||||
{
|
||||
Vector4 _u = u - a, _v = b - a;
|
||||
float t = Types::Clamp(_u.Dot(_v) / _v.Dot(_v));
|
||||
|
||||
if (p)
|
||||
p[0] = _v * t + a;
|
||||
return t;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} // Geometric
|
||||
} // GS
|
||||
46
include/framework/geometry/plane.cpp
Normal file
46
include/framework/geometry/plane.cpp
Normal file
@ -0,0 +1,46 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "geometry/plane.h"
|
||||
#include "math/matrix4.h"
|
||||
|
||||
using namespace GS;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Plane::Set(const Vector4 *_p, const Vector4 &_n, const Matrix4 *mtx)
|
||||
{
|
||||
if (mtx)
|
||||
{
|
||||
if (_p)
|
||||
mtx->Apply(&p, _p);
|
||||
else p = mtx->GetRow(3);
|
||||
mtx->ApplyRotation(&n, &_n);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_p)
|
||||
p = *_p;
|
||||
else p.Set(0, 0, 0, 1);
|
||||
n = _n;
|
||||
}
|
||||
d = -p.Dot(n);
|
||||
}
|
||||
void Plane::Set(const Vector4 _p[3], const Matrix4 *mtx)
|
||||
{
|
||||
Vector4 _n = (_p[1] - _p[0]).Cross(_p[2] - _p[0]);
|
||||
Set(&_p[0], _n, mtx);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Plane::Plane()
|
||||
{
|
||||
d = 0;
|
||||
p.Set();
|
||||
n.Set();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
54
include/framework/geometry/rect.cpp
Normal file
54
include/framework/geometry/rect.cpp
Normal file
@ -0,0 +1,54 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "geometry/rect.h"
|
||||
#include "metafile/nml.h"
|
||||
|
||||
using namespace GS;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
template <class T> NML::Tag *Rect<T>::AsMetaTag(const char *id) const
|
||||
{
|
||||
NML::Tag *root = new NML::Tag(id ? id : "Rect");
|
||||
|
||||
if (root)
|
||||
{
|
||||
root->AddChild("SX", sx);
|
||||
root->AddChild("SY", sy);
|
||||
root->AddChild("EX", ex);
|
||||
root->AddChild("EY", ey);
|
||||
}
|
||||
return root;
|
||||
}
|
||||
template <class T> bool Rect<T>::FromMetaTag(NML::Tag &tag)
|
||||
{
|
||||
NML::Tag *t;
|
||||
List <NML::Tag *> ::Iterator i(tag.GetTags().GetRoot());
|
||||
|
||||
t = i.ObjectPtr();
|
||||
if (!t) return false;
|
||||
sx = t->GetReal();
|
||||
++i;
|
||||
|
||||
t = i.ObjectPtr();
|
||||
if (!t) return false;
|
||||
sy = t->GetReal();
|
||||
++i;
|
||||
|
||||
t = i.ObjectPtr();
|
||||
if (!t) return false;
|
||||
ex = t->GetReal();
|
||||
++i;
|
||||
|
||||
t = i.ObjectPtr();
|
||||
if (!t) return false;
|
||||
ey = t->GetReal();
|
||||
++i;
|
||||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
319
include/framework/math/matrix3.cpp
Normal file
319
include/framework/math/matrix3.cpp
Normal file
@ -0,0 +1,319 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include <cmath>
|
||||
#include "math/matrix3.h"
|
||||
#include "math/matrix4.h"
|
||||
#include "metafile/nml.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::Math;
|
||||
|
||||
Matrix3 Matrix3::static_identity;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Matrix3::Inverse(Matrix3 &i) const
|
||||
{
|
||||
// Covariants.
|
||||
i.m[0][0] = m[1][1] * m[2][2] - m[1][2] * m[2][1];
|
||||
i.m[0][1] = m[0][2] * m[2][1] - m[0][1] * m[2][2];
|
||||
i.m[0][2] = m[0][1] * m[1][2] - m[0][2] * m[1][1];
|
||||
i.m[1][0] = m[1][2] * m[2][0] - m[1][0] * m[2][2];
|
||||
i.m[1][1] = m[0][0] * m[2][2] - m[0][2] * m[2][0];
|
||||
i.m[1][2] = m[0][2] * m[1][0] - m[0][0] * m[1][2];
|
||||
i.m[2][0] = m[1][0] * m[2][1] - m[1][1] * m[2][0];
|
||||
i.m[2][1] = m[0][1] * m[2][0] - m[0][0] * m[2][1];
|
||||
i.m[2][2] = m[0][0] * m[1][1] - m[0][1] * m[1][0];
|
||||
|
||||
float k = m[0][0] * i.m[0][0] + m[0][1] * i.m[1][0] + m[0][2] * i.m[2][0];
|
||||
if (!k)
|
||||
return false;
|
||||
|
||||
k = 1.f / k;
|
||||
i.m[0][0] *= k; i.m[0][1] *= k; i.m[0][2] *= k;
|
||||
i.m[1][0] *= k; i.m[1][1] *= k; i.m[1][2] *= k;
|
||||
i.m[2][0] *= k; i.m[2][1] *= k; i.m[2][2] *= k;
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Matrix3 Matrix3::VectorMatrix(const Vector4 &v)
|
||||
{ return Matrix3(v.x, 0, 0, v.y, 0, 0, v.z, 0, 0); }
|
||||
Matrix3 Matrix3::CrossProductMatrix(const Vector4 &v)
|
||||
{ return Matrix3(0, -v.z, v.y, v.z, 0, -v.x, -v.y, v.x, 0); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Matrix3 Matrix3::Normalized() const
|
||||
{
|
||||
Vector4 x(GetRow(0)), y(GetRow(1)), z(GetRow(2));
|
||||
|
||||
Matrix3 m;
|
||||
m.SetRow(0, x.Normalized());
|
||||
m.SetRow(1, y.Normalized());
|
||||
m.SetRow(2, z.Normalized());
|
||||
return m;
|
||||
}
|
||||
Matrix3 Matrix3::AsOrthonormalBase() const
|
||||
{
|
||||
Vector4 x(GetRow(0)), y(GetRow(1));
|
||||
|
||||
Matrix3 m;
|
||||
x = x.Normalized();
|
||||
m.SetRow(0, x);
|
||||
Vector4 z(x.Cross(y).Normalized());
|
||||
m.SetRow(2, z);
|
||||
y = z.Cross(x).Normalized();
|
||||
m.SetRow(1, y);
|
||||
return m;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Vector4 Matrix3::AsEuler(rOrder rorder) const
|
||||
{
|
||||
Vector4 euler(0, 0, 0);
|
||||
|
||||
switch (rorder)
|
||||
{
|
||||
case rOrder_ZYX:
|
||||
euler.y = ASin(-m[2][0]);
|
||||
euler.z = atan2(m[1][0], m[0][0]);
|
||||
euler.x = atan2(m[2][1], m[2][2]);
|
||||
break;
|
||||
|
||||
case rOrder_XZY:
|
||||
euler.z = ASin(-m[0][1]);
|
||||
euler.x = atan2(m[2][1], m[1][1]);
|
||||
euler.y = atan2(m[0][2], m[0][0]);
|
||||
break;
|
||||
|
||||
case rOrder_XYZ:
|
||||
euler.y = ASin(m[0][2]);
|
||||
euler.x = atan2(-m[1][2], m[2][2]);
|
||||
euler.z = atan2(-m[0][1], m[0][0]);
|
||||
break;
|
||||
|
||||
case rOrder_YZX:
|
||||
euler.z = ASin(m[1][0]);
|
||||
euler.x = atan2(-m[1][2], m[1][1]);
|
||||
euler.y = atan2(-m[2][0], m[0][0]);
|
||||
break;
|
||||
|
||||
default:
|
||||
case rOrder_YXZ: // Engine default.
|
||||
euler.x = ASin(-m[1][2]);
|
||||
euler.y = atan2(m[0][2], m[2][2]);
|
||||
euler.z = atan2(m[1][0], m[1][1]);
|
||||
break;
|
||||
|
||||
case rOrder_ZXY: // MAX default.
|
||||
euler.x = ASin(m[2][1]);
|
||||
euler.y = atan2(-m[2][0], m[2][2]);
|
||||
euler.z = atan2(-m[0][1], m[1][1]);
|
||||
break;
|
||||
|
||||
case rOrder_XY:
|
||||
euler.y = ACos(m[0][0]);
|
||||
euler.x = ACos(m[1][1]);
|
||||
euler.z = 0;
|
||||
break;
|
||||
}
|
||||
return euler;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Matrix3 Matrix3::FromEuler(const Vector4 &euler, rOrder rorder)
|
||||
{ return Matrix3::FromEuler(euler.x, euler.y, euler.z, rorder); }
|
||||
Matrix3 Matrix3::FromEuler(float x, float y, float z, rOrder rorder)
|
||||
{
|
||||
float cx = Cos(x), cy = Cos(y), cz = Cos(z),
|
||||
sx = Sin(x), sy = Sin(y), sz = Sin(z);
|
||||
|
||||
switch (rorder)
|
||||
{
|
||||
case rOrder_XZY:
|
||||
return Matrix3 ( cy * cz, sx * sy + cx * cy * sz, -cx * sy + cy * sx * sz,
|
||||
-sz, cx * cz, cz * sx,
|
||||
cz * sy, -cy * sx + cx * sy * sz, cx * cy + sx * sy * sz );
|
||||
|
||||
case rOrder_ZYX:
|
||||
return Matrix3 ( cy * cz, cy * sz, -sy,
|
||||
cz * sx * sy - cx * sz, cx * cz + sx * sy * sz, cy * sx,
|
||||
cx *cz * sy + sx * sz, -cz * sx + cx * sy * sz, cx * cy );
|
||||
|
||||
case rOrder_XYZ:
|
||||
return Matrix3 ( cy * cz, cz * sx * sy + cx * sz, -cx * cz * sy + sx * sz,
|
||||
-cy * sz, cx * cz - sx * sy * sz, cz * sx + cx * sy * sz,
|
||||
sy, -cy * sx, cx * cy );
|
||||
|
||||
case rOrder_ZXY:
|
||||
return Matrix3 ( cy * cz - sx * sy * sz, cz * sx * sy + cy * sz, -cx * sy,
|
||||
-cx * sz, cx * cz, sx,
|
||||
cz * sy + cy * sx * sz, -cy * cz * sx + sy * sz, cx * cy );
|
||||
|
||||
case rOrder_YZX:
|
||||
return Matrix3 ( cy * cz, sz, -cz * sy,
|
||||
sx * sy - cx * cy * sz, cx * cz, cy * sx + cx * sy * sz,
|
||||
cx * sy + cy * sx * sz, -cz * sx, cx * cy - sx * sy * sz );
|
||||
|
||||
case rOrder_YXZ:
|
||||
return Matrix3 ( cy * cz + sx * sy * sz, cx * sz, -cz * sy + cy * sx * sz,
|
||||
cz * sx * sy - cy * sz, cx * cz, cy * cz * sx + sy * sz,
|
||||
cx * sy, -sx, cx * cy );
|
||||
|
||||
case rOrder_XY:
|
||||
return Matrix3 ( cy, sx * sy, -cx * sy,
|
||||
0, cx, sx,
|
||||
sy, -cy * sx, cx * cy );
|
||||
}
|
||||
return Matrix3::IdentityMatrix();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Matrix3 Matrix3::TranslationMatrix(const Vector4 &t)
|
||||
{ return Matrix3(1, 0, 0, 0, 1, 0, t.x, t.y, 1); }
|
||||
Matrix3 Matrix3::TranslationMatrix(const Vector2 &t)
|
||||
{ return Matrix3(1, 0, 0, 0, 1, 0, t.x, t.y, 1); }
|
||||
Matrix3 Matrix3::ScaleMatrix(const Vector4 &s)
|
||||
{ return Matrix3(s.x, 0, 0, 0, s.y, 0, 0, 0, s.z); }
|
||||
Matrix3 Matrix3::ScaleMatrix(const Vector2 &s)
|
||||
{ return Matrix3(s.x, 0, 0, 0, s.y, 0, 0, 0, 1); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Matrix3 Matrix3::RotationMatrixXAxis(float a)
|
||||
{ return Matrix3(1, 0, 0, 0, Cos(a), Sin(a), 0, -Sin(a), Cos(a)); }
|
||||
Matrix3 Matrix3::RotationMatrixYAxis(float a)
|
||||
{ return Matrix3(Cos(a), 0, -Sin(a), 0, 1, 0, Sin(a), 0, Cos(a)); }
|
||||
Matrix3 Matrix3::RotationMatrixZAxis(float a)
|
||||
{ return Matrix3(Cos(a), Sin(a), 0, -Sin(a), Cos(a), 0, 0, 0, 1); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Matrix3::SetRow(uint n, const Vector4 &row)
|
||||
{ m[0][n] = row.x; m[1][n] = row.y; m[2][n] = row.z; }
|
||||
void Matrix3::SetColumn(uint n, const Vector4 &col)
|
||||
{ m[n][0] = col.x; m[n][1] = col.y; m[n][2] = col.z; }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Matrix3 Matrix3::FromOrthonormalBasis(const Vector4 &w, const Vector4 *v)
|
||||
{
|
||||
Matrix3 mtx;
|
||||
|
||||
float l = w.Len();
|
||||
if (!l)
|
||||
return Matrix3::IdentityMatrix();
|
||||
|
||||
Vector4 wn = w / l, u;
|
||||
|
||||
if (!v)
|
||||
{
|
||||
if (!EqualZero(wn.x) || !EqualZero(wn.z))
|
||||
{
|
||||
u.Set(wn.z, 0, -wn.x); // Cross with up = {0,1,0}.
|
||||
u = u.Normalized();
|
||||
}
|
||||
else
|
||||
u.Set(-1, 0, 0);
|
||||
|
||||
Vector4 c(wn.Cross(u));
|
||||
mtx.SetRow(1, c);
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector4 vn(v->Normalized());
|
||||
mtx.SetRow(1, vn);
|
||||
u = vn.Cross(wn);
|
||||
}
|
||||
|
||||
mtx.SetRow(0, u);
|
||||
mtx.SetRow(2, wn);
|
||||
return mtx;
|
||||
}
|
||||
Matrix3 Matrix3::FromMatrix4(const Matrix4 &mtx)
|
||||
{
|
||||
return Matrix3(
|
||||
mtx.m[0][0], mtx.m[1][0], mtx.m[2][0],
|
||||
mtx.m[0][1], mtx.m[1][1], mtx.m[2][1],
|
||||
mtx.m[0][2], mtx.m[1][2], mtx.m[2][2]
|
||||
);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void Matrix3::Apply(Vector4 *o, const Vector4 *v, uint n) const
|
||||
//-----------------------------------------------------------------------------
|
||||
{
|
||||
for (uint c = 0; c < n; c++)
|
||||
{
|
||||
float x = v->x, y = v->y, z = v->z;
|
||||
o->x = x * m[0][0] + y * m[0][1] + z * m[0][2];
|
||||
o->y = x * m[1][0] + y * m[1][1] + z * m[1][2];
|
||||
o->z = x * m[2][0] + y * m[2][1] + z * m[2][2];
|
||||
o->w = 1;
|
||||
o++; v++;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Matrix3::Set
|
||||
(
|
||||
float m00, float m10, float m20,
|
||||
float m01, float m11, float m21,
|
||||
float m02, float m12, float m22
|
||||
)
|
||||
{
|
||||
m[0][0] = m00; m[1][0] = m10; m[2][0] = m20;
|
||||
m[0][1] = m01; m[1][1] = m11; m[2][1] = m21;
|
||||
m[0][2] = m02; m[1][2] = m12; m[2][2] = m22;
|
||||
}
|
||||
void Matrix3::Set(const Vector4 &u, const Vector4 &v, const Vector4 &w)
|
||||
{
|
||||
m[0][0] = u.x; m[1][0] = u.y; m[2][0] = u.z;
|
||||
m[0][1] = v.x; m[1][1] = v.y; m[2][1] = v.z;
|
||||
m[0][2] = w.x; m[1][2] = w.y; m[2][2] = w.z;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
NML::Tag *Matrix3::AsMetaTag(const char *id) const
|
||||
{
|
||||
NML::Tag *root = new NML::Tag(id ? id : "Mtx3");
|
||||
root->AddChild(GetRow(0).AsMetaTag("R0"));
|
||||
root->AddChild(GetRow(1).AsMetaTag("R1"));
|
||||
root->AddChild(GetRow(2).AsMetaTag("R2"));
|
||||
return root;
|
||||
}
|
||||
bool Matrix3::FromMetaTag(NML::Tag &tag)
|
||||
{
|
||||
NML::Tag *t;
|
||||
Vector4 R;
|
||||
|
||||
List <NML::Tag *> ::Iterator i(tag.GetTags().GetRoot());
|
||||
|
||||
t = i.ObjectPtr();
|
||||
if (!t) return false;
|
||||
R.FromMetaTag(*t); SetRow(0, R);
|
||||
++i;
|
||||
|
||||
t = i.ObjectPtr();
|
||||
if (!t) return false;
|
||||
R.FromMetaTag(*t); SetRow(1, R);
|
||||
++i;
|
||||
|
||||
t = i.ObjectPtr();
|
||||
if (!t) return false;
|
||||
R.FromMetaTag(*t); SetRow(2, R);
|
||||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
277
include/framework/math/matrix4.cpp
Normal file
277
include/framework/math/matrix4.cpp
Normal file
@ -0,0 +1,277 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
------------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
#include "math/matrix4.h"
|
||||
#include "math/matrix3.h"
|
||||
#include "math/quaternion.h"
|
||||
#include "metafile/nml.h"
|
||||
|
||||
using namespace GS;
|
||||
|
||||
Matrix4 Matrix4::static_identity(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Matrix4 Matrix4::FromMatrix3(const Matrix3 &m)
|
||||
{
|
||||
return Matrix4(
|
||||
m.m[0][0], m.m[1][0], m.m[2][0], 0,
|
||||
m.m[0][1], m.m[1][1], m.m[2][1], 0,
|
||||
m.m[0][2], m.m[1][2], m.m[2][2], 0,
|
||||
0, 0, 0, 1
|
||||
);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
const Matrix4 &Matrix4WithInverse::Get() const
|
||||
{ return matrix; }
|
||||
const Matrix4 &Matrix4WithInverse::GetInverse() const
|
||||
{ return imatrix; }
|
||||
void Matrix4WithInverse::Commit()
|
||||
{ imatrix = matrix.InversedFast(); }
|
||||
void Matrix4WithInverse::Set(const Matrix4 &m)
|
||||
{
|
||||
matrix = m;
|
||||
Commit();
|
||||
}
|
||||
Vector4 Matrix4WithInverse::GetRow(uint n, bool w_1) const
|
||||
{ return matrix.GetRow(n, w_1); }
|
||||
Vector4 Matrix4WithInverse::GetColumn(uint n, bool w_1) const
|
||||
{ return matrix.GetColumn(n, w_1); }
|
||||
void Matrix4WithInverse::SetRow(uint n, const Vector4 &row, bool w_1)
|
||||
{
|
||||
matrix.SetRow(n, row, w_1);
|
||||
Commit();
|
||||
}
|
||||
void Matrix4WithInverse::SetColumn(uint n, const Vector4 &col, bool w_1)
|
||||
{
|
||||
matrix.SetColumn(n, col, w_1);
|
||||
Commit();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
NML::Tag *Matrix4WithInverse::AsMetaTag(const char *id) const
|
||||
{ return matrix.AsMetaTag(id); }
|
||||
bool Matrix4WithInverse::FromMetaTag(NML::Tag &tag)
|
||||
{
|
||||
if (!matrix.FromMetaTag(tag))
|
||||
return false;
|
||||
Commit();
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Matrix4 Matrix4::TransformationMatrix(const Vector4 &p, const Matrix3 &r, const Vector4 &s, const Vector4 *o)
|
||||
{
|
||||
Matrix4 m =
|
||||
Matrix4::TranslationMatrix(p) *
|
||||
Matrix4::FromMatrix3(r) *
|
||||
Matrix4::ScaleMatrix(s);
|
||||
return o ? m * Matrix4::TranslationMatrix(*o) : m;
|
||||
}
|
||||
Matrix4 Matrix4::TransformationMatrix(const Vector4 &p, const Vector4 &r, const Vector4 &s, const Vector4 *o)
|
||||
{
|
||||
Matrix4 m =
|
||||
Matrix4::TranslationMatrix(p) *
|
||||
Matrix4::FromMatrix3(Matrix3::FromEuler(r.x, r.y, r.z)) *
|
||||
Matrix4::ScaleMatrix(s);
|
||||
return o ? m * Matrix4::TranslationMatrix(*o) : m;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Matrix4 Matrix4::LerpAsOrthonormalBase(const Matrix4 &a, const Matrix4 &b, float k, bool fast)
|
||||
{
|
||||
if (fast)
|
||||
{
|
||||
Matrix4 o;
|
||||
for (int m = 0; m < 4; ++m)
|
||||
for (int n = 0; n < 4; ++n)
|
||||
o.m[m][n] = (b.m[m][n] - a.m[m][n]) * k + a.m[m][n];
|
||||
return o;
|
||||
}
|
||||
|
||||
Matrix3 a_matrix3, b_matrix3;
|
||||
Vector4 a_position, b_position, a_scale, b_scale;
|
||||
|
||||
a.Decompose(&a_position, &a_scale, &a_matrix3);
|
||||
b.Decompose(&b_position, &b_scale, &b_matrix3);
|
||||
|
||||
Quaternion a_orientation(Quaternion::FromMatrix3(a_matrix3));
|
||||
Quaternion b_orientation(Quaternion::FromMatrix3(b_matrix3));
|
||||
|
||||
return Matrix4::TranslationMatrix((b_position - a_position) * k + a_position) *
|
||||
Matrix4::FromMatrix3(Quaternion::Slerp(k, a_orientation, b_orientation).AsMatrix3()) *
|
||||
Matrix4::ScaleMatrix((b_scale - a_scale) * k + a_scale);
|
||||
}
|
||||
void Matrix4::Decompose(Vector4 *position, Vector4 *scale, Vector4 *rotation, Math::rOrder order) const
|
||||
{
|
||||
Matrix3 m3;
|
||||
Decompose(position, scale, &m3);
|
||||
if (rotation)
|
||||
*rotation = m3.AsEuler(order);
|
||||
}
|
||||
void Matrix4::Decompose(Vector4 *position, Vector4 *scale, Matrix3 *rotation) const
|
||||
{
|
||||
// Extract position.
|
||||
if (position)
|
||||
*position = GetRow(3);
|
||||
|
||||
// Extract scale.
|
||||
Vector4 scl;
|
||||
scl.Set(GetRow(0).Len(), GetRow(1).Len(), GetRow(2).Len());
|
||||
|
||||
// Handle negative scale (permute X to preserve left-handedness).
|
||||
Vector4 left = GetRow(1).Cross(GetRow(2));
|
||||
if (left.Dot(GetRow(0)) < 0)
|
||||
scl.x = -scl.x;
|
||||
if (scale)
|
||||
*scale = scl;
|
||||
|
||||
// Rotation 3x3 (renormalized).
|
||||
if (rotation)
|
||||
{
|
||||
if (scl.x)
|
||||
{
|
||||
scl.x = 1 / scl.x;
|
||||
rotation->SetRow(0, Vector4(m[0][0] * scl.x, m[1][0] * scl.x, m[2][0] * scl.x));
|
||||
}
|
||||
else rotation->SetRow(0, Vector4(1, 0, 0));
|
||||
|
||||
if (scl.y)
|
||||
{
|
||||
scl.y = 1 / scl.y;
|
||||
rotation->SetRow(1, Vector4(m[0][1] * scl.y, m[1][1] * scl.y, m[2][1] * scl.y));
|
||||
}
|
||||
else rotation->SetRow(1, Vector4(0, 1, 0));
|
||||
|
||||
if (scl.z)
|
||||
{
|
||||
scl.z = 1 / scl.z;
|
||||
rotation->SetRow(2, Vector4(m[0][2] * scl.z, m[1][2] * scl.z, m[2][2] * scl.z));
|
||||
}
|
||||
else rotation->SetRow(2, Vector4(0, 0, 1));
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Matrix4 Matrix4::InversedFast() const
|
||||
{
|
||||
// Extract inverse scale.
|
||||
Vector4 scl(1.f / GetRow(0).Len(), 1.f / GetRow(1).Len(), 1.f / GetRow(2).Len());
|
||||
|
||||
// Inverse rotation 3x3 (renormalized).
|
||||
Matrix3 irt (
|
||||
m[0][0] * scl.x, m[0][1] * scl.y, m[0][2] * scl.z,
|
||||
m[1][0] * scl.x, m[1][1] * scl.y, m[1][2] * scl.z,
|
||||
m[2][0] * scl.x, m[2][1] * scl.y, m[2][2] * scl.z
|
||||
);
|
||||
|
||||
// Recompose as inverse matrix.
|
||||
return Matrix4::ScaleMatrix(scl) * (Matrix4::FromMatrix3(irt) * Matrix4::TranslationMatrix(GetRow(3).Reversed()));
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Matrix4 Matrix4::AsOrthonormalBase() const
|
||||
{
|
||||
Matrix3 rcp (
|
||||
m[0][0], m[1][0], m[2][0],
|
||||
m[0][1], m[1][1], m[2][1],
|
||||
m[0][2], m[1][2], m[2][2]
|
||||
);
|
||||
rcp = rcp.AsOrthonormalBase();
|
||||
|
||||
Matrix4 otb(*this);
|
||||
for (int i = 0; i < 3; ++i)
|
||||
for (int j = 0; j < 3; ++j)
|
||||
otb.m[i][j] = rcp.m[i][j];
|
||||
return otb;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Matrix4 Matrix4::TranslationMatrix(const Vector4 &t)
|
||||
{ return Matrix4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, t.x, t.y, t.z, 1); }
|
||||
Matrix4 Matrix4::ScaleMatrix(const Vector4 &s)
|
||||
{ return Matrix4(s.x, 0, 0, 0, 0, s.y, 0, 0, 0, 0, s.z, 0, 0, 0, 0, 1); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
NML::Tag *Matrix4::AsMetaTag(const char *id) const
|
||||
{
|
||||
NML::Tag *root = new NML::Tag(id ? id : "Mtx4");
|
||||
root->AddChild(GetRow(0, false).AsMetaTag("R0", true));
|
||||
root->AddChild(GetRow(1, false).AsMetaTag("R1", true));
|
||||
root->AddChild(GetRow(2, false).AsMetaTag("R2", true));
|
||||
root->AddChild(GetRow(3, false).AsMetaTag("R3", true));
|
||||
return root;
|
||||
}
|
||||
bool Matrix4::FromMetaTag(NML::Tag &tag)
|
||||
{
|
||||
NML::Tag *t;
|
||||
Vector4 R;
|
||||
|
||||
List <NML::Tag *> ::Iterator i(tag.GetTags().GetRoot());
|
||||
|
||||
t = i.ObjectPtr();
|
||||
if (!t) return false;
|
||||
R.FromMetaTag(*t); SetRow(0, R, false);
|
||||
++i;
|
||||
|
||||
t = i.ObjectPtr();
|
||||
if (!t) return false;
|
||||
R.FromMetaTag(*t); SetRow(1, R, false);
|
||||
++i;
|
||||
|
||||
t = i.ObjectPtr();
|
||||
if (!t) return false;
|
||||
R.FromMetaTag(*t); SetRow(2, R, false);
|
||||
++i;
|
||||
|
||||
t = i.ObjectPtr();
|
||||
if (!t) return false;
|
||||
R.FromMetaTag(*t); SetRow(3, R, false);
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool Matrix4::Inverse(Matrix4 &out) const
|
||||
{
|
||||
float inv[16], det;
|
||||
|
||||
inv[0] = m[1][1] * m[2][2] * m[3][3] - m[1][1] * m[2][3] * m[3][2] - m[2][1] * m[1][2] * m[3][3] + m[2][1] * m[1][3] * m[3][2] + m[3][1] * m[1][2] * m[2][3] - m[3][1] * m[1][3] * m[2][2];
|
||||
inv[4] = -m[1][0] * m[2][2] * m[3][3] + m[1][0] * m[2][3] * m[3][2] + m[2][0] * m[1][2] * m[3][3] - m[2][0] * m[1][3] * m[3][2] - m[3][0] * m[1][2] * m[2][3] + m[3][0] * m[1][3] * m[2][2];
|
||||
inv[8] = m[1][0] * m[2][1] * m[3][3] - m[1][0] * m[2][3] * m[3][1] - m[2][0] * m[1][1] * m[3][3] + m[2][0] * m[1][3] * m[3][1] + m[3][0] * m[1][1] * m[2][3] - m[3][0] * m[1][3] * m[2][1];
|
||||
inv[12] = -m[1][0] * m[2][1] * m[3][2] + m[1][0] * m[2][2] * m[3][1] + m[2][0] * m[1][1] * m[3][2] - m[2][0] * m[1][2] * m[3][1] - m[3][0] * m[1][1] * m[2][2] + m[3][0] * m[1][2] * m[2][1];
|
||||
inv[1] = -m[0][1] * m[2][2] * m[3][3] + m[0][1] * m[2][3] * m[3][2] + m[2][1] * m[0][2] * m[3][3] - m[2][1] * m[0][3] * m[3][2] - m[3][1] * m[0][2] * m[2][3] + m[3][1] * m[0][3] * m[2][2];
|
||||
inv[5] = m[0][0] * m[2][2] * m[3][3] - m[0][0] * m[2][3] * m[3][2] - m[2][0] * m[0][2] * m[3][3] + m[2][0] * m[0][3] * m[3][2] + m[3][0] * m[0][2] * m[2][3] - m[3][0] * m[0][3] * m[2][2];
|
||||
inv[9] = -m[0][0] * m[2][1] * m[3][3] + m[0][0] * m[2][3] * m[3][1] + m[2][0] * m[0][1] * m[3][3] - m[2][0] * m[0][3] * m[3][1] - m[3][0] * m[0][1] * m[2][3] + m[3][0] * m[0][3] * m[2][1];
|
||||
inv[13] = m[0][0] * m[2][1] * m[3][2] - m[0][0] * m[2][2] * m[3][1] - m[2][0] * m[0][1] * m[3][2] + m[2][0] * m[0][2] * m[3][1] + m[3][0] * m[0][1] * m[2][2] - m[3][0] * m[0][2] * m[2][1];
|
||||
inv[2] = m[0][1] * m[1][2] * m[3][3] - m[0][1] * m[1][3] * m[3][2] - m[1][1] * m[0][2] * m[3][3] + m[1][1] * m[0][3] * m[3][2] + m[3][1] * m[0][2] * m[1][3] - m[3][1] * m[0][3] * m[1][2];
|
||||
inv[6] = -m[0][0] * m[1][2] * m[3][3] + m[0][0] * m[1][3] * m[3][2] + m[1][0] * m[0][2] * m[3][3] - m[1][0] * m[0][3] * m[3][2] - m[3][0] * m[0][2] * m[1][3] + m[3][0] * m[0][3] * m[1][2];
|
||||
inv[10] = m[0][0] * m[1][1] * m[3][3] - m[0][0] * m[1][3] * m[3][1] - m[1][0] * m[0][1] * m[3][3] + m[1][0] * m[0][3] * m[3][1] + m[3][0] * m[0][1] * m[1][3] - m[3][0] * m[0][3] * m[1][1];
|
||||
inv[14] = -m[0][0] * m[1][1] * m[3][2] + m[0][0] * m[1][2] * m[3][1] + m[1][0] * m[0][1] * m[3][2] - m[1][0] * m[0][2] * m[3][1] - m[3][0] * m[0][1] * m[1][2] + m[3][0] * m[0][2] * m[1][1];
|
||||
inv[3] = -m[0][1] * m[1][2] * m[2][3] + m[0][1] * m[1][3] * m[2][2] + m[1][1] * m[0][2] * m[2][3] - m[1][1] * m[0][3] * m[2][2] - m[2][1] * m[0][2] * m[1][3] + m[2][1] * m[0][3] * m[1][2];
|
||||
inv[7] = m[0][0] * m[1][2] * m[2][3] - m[0][0] * m[1][3] * m[2][2] - m[1][0] * m[0][2] * m[2][3] + m[1][0] * m[0][3] * m[2][2] + m[2][0] * m[0][2] * m[1][3] - m[2][0] * m[0][3] * m[1][2];
|
||||
inv[11] = -m[0][0] * m[1][1] * m[2][3] + m[0][0] * m[1][3] * m[2][1] + m[1][0] * m[0][1] * m[2][3] - m[1][0] * m[0][3] * m[2][1] - m[2][0] * m[0][1] * m[1][3] + m[2][0] * m[0][3] * m[1][1];
|
||||
inv[15] = m[0][0] * m[1][1] * m[2][2] - m[0][0] * m[1][2] * m[2][1] - m[1][0] * m[0][1] * m[2][2] + m[1][0] * m[0][2] * m[2][1] + m[2][0] * m[0][1] * m[1][2] - m[2][0] * m[0][2] * m[1][1];
|
||||
det = m[0][0] * inv[0] + m[0][1] * inv[4] + m[0][2] * inv[8] + m[0][3] * inv[12];
|
||||
|
||||
if (det == 0)
|
||||
return false;
|
||||
|
||||
det = 1.f / det;
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
((float *)out.m)[i] = inv[i] * det;
|
||||
|
||||
return true;
|
||||
}
|
||||
218
include/framework/math/quaternion.cpp
Normal file
218
include/framework/math/quaternion.cpp
Normal file
@ -0,0 +1,218 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "math/quaternion.h"
|
||||
#include "math/matrix3.h"
|
||||
#include "metafile/nml.h"
|
||||
|
||||
using namespace GS;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Quaternion Quaternion::Slerp(float t, const Quaternion &a, const Quaternion &b)
|
||||
{
|
||||
float norm = a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
|
||||
|
||||
bool bFlip = false;
|
||||
|
||||
if (norm < 0.0f)
|
||||
{
|
||||
norm = -norm;
|
||||
bFlip = true;
|
||||
}
|
||||
|
||||
float inv_d;
|
||||
if (1.0f - norm < 0.000001f)
|
||||
inv_d = 1.0f - t;
|
||||
|
||||
else
|
||||
{
|
||||
float theta = Math::ACos(norm);
|
||||
float s = 1.f / Math::Sin(theta);
|
||||
|
||||
inv_d = Math::Sin((1.0f - t) * theta) * s;
|
||||
t = Math::Sin(t * theta) * s;
|
||||
}
|
||||
|
||||
if (bFlip)
|
||||
t = -t;
|
||||
|
||||
return Quaternion(inv_d * a.x + t * b.x, inv_d * a.y + t * b.y, inv_d * a.z + t * b.z, inv_d * a.w + t * b.w);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
float Quaternion::Distance(const Quaternion &a, const Quaternion &b)
|
||||
{
|
||||
const float dx = a.x - b.x, dy = a.y - b.y, dz = a.z - b.z, dw = a.w - b.w;
|
||||
return Math::Sqrt((dx * dx) + (dy * dy) + (dz * dz) + (dw * dw));
|
||||
}
|
||||
Quaternion Quaternion::Inverse() const
|
||||
{
|
||||
const float norm = w * w + x * x + y * y + z * z;
|
||||
if (norm > 0)
|
||||
{
|
||||
const float inorm = 1.f / norm;
|
||||
return Quaternion(x * -inorm, y * -inorm, z * -inorm, w * inorm);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
Quaternion Quaternion::Normalize() const
|
||||
{
|
||||
float d = Math::Sqrt(x * x + y * y + z * z + w * w);
|
||||
if (!d)
|
||||
return Quaternion(1, 1, 1, 1);
|
||||
|
||||
float k = 1.f / d;
|
||||
return Quaternion(x * k, y * k, z * k, w * k);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Quaternion Quaternion::LookAt(const Vector4 &at)
|
||||
{ return Quaternion::FromMatrix3(Matrix3::FromOrthonormalBasis(at)); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Quaternion Quaternion::FromMatrix3(const Matrix3 &m)
|
||||
{
|
||||
// From "Quaternion Calculus and Fast Animation".
|
||||
float x, y, z, w;
|
||||
float trace = m.m[0][0] + m.m[1][1] + m.m[2][2];
|
||||
|
||||
if (trace > 0.0)
|
||||
{
|
||||
// |w| > 1/2, may as well choose w > 1/2
|
||||
float root = Math::Sqrt(trace + 1.0f); // 2w
|
||||
w = 0.5f * root;
|
||||
root = 0.5f / root; // 1/(4w)
|
||||
x = (m.m[2][1] - m.m[1][2]) * root;
|
||||
y = (m.m[0][2] - m.m[2][0]) * root;
|
||||
z = (m.m[1][0] - m.m[0][1]) * root;
|
||||
}
|
||||
else
|
||||
{
|
||||
// |w| <= 1/2
|
||||
static size_t inext[3] = { 1, 2, 0 };
|
||||
size_t i = 0;
|
||||
if (m.m[1][1] > m.m[0][0])
|
||||
i = 1;
|
||||
if (m.m[2][2] > m.m[i][i])
|
||||
i = 2;
|
||||
size_t j = inext[i];
|
||||
size_t k = inext[j];
|
||||
|
||||
float root = Math::Sqrt(m.m[i][i] - m.m[j][j] - m.m[k][k] + 1.0f);
|
||||
float *quat[3] = { &x, &y, &z };
|
||||
*quat[i] = 0.5f * root;
|
||||
root = 0.5f / root;
|
||||
w = (m.m[k][j] - m.m[j][k]) * root;
|
||||
*quat[j] = (m.m[j][i] + m.m[i][j]) * root;
|
||||
*quat[k] = (m.m[k][i] + m.m[i][k]) * root;
|
||||
}
|
||||
return Quaternion(x, y, z, w);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Quaternion Quaternion::FromAxisAngle(float a, float _x, float _y, float _z)
|
||||
{
|
||||
float sn = Math::Sin(a * 0.5f), cs = Math::Cos(a * 0.5f);
|
||||
return Quaternion(_x * sn, _y * sn, _z * sn, cs).Normalize();
|
||||
}
|
||||
Quaternion Quaternion::FromEuler(float _x, float _y, float _z, Math::rOrder rorder)
|
||||
{
|
||||
Quaternion qx(Quaternion::FromAxisAngle(_x, 1, 0, 0)),
|
||||
qy(Quaternion::FromAxisAngle(_y, 0, 1, 0)),
|
||||
qz(Quaternion::FromAxisAngle(_z, 0, 0, 1)),
|
||||
q;
|
||||
|
||||
switch (rorder)
|
||||
{
|
||||
case Math::rOrder_ZYX: q = qz * qy * qx; break;
|
||||
case Math::rOrder_YZX: q = qy * qz * qx; break;
|
||||
case Math::rOrder_ZXY: q = qz * qx * qy; break;
|
||||
case Math::rOrder_XZY: q = qx * qz * qy; break;
|
||||
default:
|
||||
case Math::rOrder_YXZ: q = qy * qx * qz; break;
|
||||
case Math::rOrder_XYZ: q = qx * qy * qz; break;
|
||||
case Math::rOrder_XY: q = qx * qy; break;
|
||||
}
|
||||
return q.Normalize();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Matrix3 Quaternion::AsMatrix3() const
|
||||
{
|
||||
float sqw = w * w, sqx = x * x, sqy = y * y, sqz = z * z;
|
||||
|
||||
Matrix3 m;
|
||||
|
||||
float invs = 1.f / (sqx + sqy + sqz + sqw);
|
||||
m.m[0][0] = ( sqx - sqy - sqz + sqw) * invs; // Since sqw + sqx + sqy + sqz = 1 / invs * invs.
|
||||
m.m[1][1] = (-sqx + sqy - sqz + sqw) * invs;
|
||||
m.m[2][2] = (-sqx - sqy + sqz + sqw) * invs;
|
||||
|
||||
float tmp1 = x * y;
|
||||
float tmp2 = z * w;
|
||||
m.m[1][0] = 2.f * (tmp1 + tmp2) * invs;
|
||||
m.m[0][1] = 2.f * (tmp1 - tmp2) * invs;
|
||||
|
||||
tmp1 = x * z;
|
||||
tmp2 = y * w;
|
||||
m.m[2][0] = 2.f * (tmp1 - tmp2) * invs;
|
||||
m.m[0][2] = 2.f * (tmp1 + tmp2) * invs;
|
||||
tmp1 = y * z;
|
||||
tmp2 = x * w;
|
||||
m.m[2][1] = 2.f * (tmp1 + tmp2) * invs;
|
||||
m.m[1][2] = 2.f * (tmp1 - tmp2) * invs;
|
||||
|
||||
return m;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
NML::Tag *Quaternion::AsMetaTag(const char *id) const
|
||||
{
|
||||
NML::Tag *root = new NML::Tag(id ? id : "Quaternion");
|
||||
|
||||
if (root)
|
||||
{
|
||||
root->AddChild("X", x);
|
||||
root->AddChild("Y", y);
|
||||
root->AddChild("Z", z);
|
||||
root->AddChild("W", w);
|
||||
}
|
||||
return root;
|
||||
}
|
||||
bool Quaternion::FromMetaTag(NML::Tag &tag)
|
||||
{
|
||||
NML::Tag *t;
|
||||
List <NML::Tag *> ::Iterator i(tag.GetTags().GetRoot());
|
||||
|
||||
t = i.ObjectPtr();
|
||||
if (!t) return false;
|
||||
x = t->GetReal();
|
||||
++i;
|
||||
|
||||
t = i.ObjectPtr();
|
||||
if (!t) return false;
|
||||
y = t->GetReal();
|
||||
++i;
|
||||
|
||||
t = i.ObjectPtr();
|
||||
if (!t) return false;
|
||||
z = t->GetReal();
|
||||
++i;
|
||||
|
||||
t = i.ObjectPtr();
|
||||
if (!t) return false;
|
||||
w = t->GetReal();
|
||||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
207
include/framework/math/vector.cpp
Normal file
207
include/framework/math/vector.cpp
Normal file
@ -0,0 +1,207 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "metafile/nml.h"
|
||||
#include "math/matrix3.h"
|
||||
#include "math/matrix4.h"
|
||||
#include "rand/rand.h"
|
||||
|
||||
using namespace GS;
|
||||
|
||||
|
||||
namespace GS {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
template <> tVector2 <int> tVector2 <int> ::operator * (const Matrix3 &m) const
|
||||
{
|
||||
return tVector2 <int> ( (float(x) * m.m[0][0] + float(y) * m.m[0][1] + m.m[0][2]),
|
||||
int(float(x) * m.m[1][0] + float(y) * m.m[1][1] + m.m[1][2]) );
|
||||
}
|
||||
template <> tVector2 <float> tVector2 <float> ::operator * (const Matrix3 &m) const
|
||||
{
|
||||
return tVector2 <float> ( x * m.m[0][0] + y * m.m[0][1] + m.m[0][2],
|
||||
x * m.m[1][0] + y * m.m[1][1] + m.m[1][2] );
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Vector4 Vector4::Floor() const
|
||||
{ return Vector4(Math::Floor(x), Math::Floor(y), Math::Floor(z), Math::Floor(w)); }
|
||||
Vector4 Vector4::Ceil() const
|
||||
{ return Vector4(Math::Ceil(x), Math::Ceil(y), Math::Ceil(z), Math::Ceil(w)); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Vector4 Vector4::Abs() const
|
||||
{ return Vector4(Types::Abs(x), Types::Abs(y), Types::Abs(z)); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Vector4 Vector4::Clamped(float min, float max) const
|
||||
{
|
||||
float _x, _y, _z;
|
||||
if (x < min) _x = min; else if (x > max) _x = max; else _x = x;
|
||||
if (y < min) _y = min; else if (y > max) _y = max; else _y = y;
|
||||
if (z < min) _z = min; else if (z > max) _z = max; else _z = z;
|
||||
return Vector4(_x, _y, _z);
|
||||
}
|
||||
Vector4 Vector4::Clamped(const Vector4 &min, const Vector4 &max) const
|
||||
{
|
||||
float _x, _y, _z;
|
||||
if (x < min.x) _x = min.x; else if (x > max.x) _x = max.x; else _x = x;
|
||||
if (y < min.y) _y = min.y; else if (y > max.y) _y = max.y; else _y = y;
|
||||
if (z < min.z) _z = min.z; else if (z > max.z) _z = max.z; else _z = z;
|
||||
return Vector4(_x, _y, _z);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Vector4 Vector4::ClampedMagnitude(float min, float max) const
|
||||
{
|
||||
float l2 = Len2();
|
||||
if ((l2 >= (min * min)) && (l2 <= (max * max)))
|
||||
return Vector4(*this);
|
||||
if (l2 < 0.000001)
|
||||
return Vector4(*this);
|
||||
float l = Math::Sqrt((float)l2);
|
||||
return (*this) * Types::Clamp(l, min, max) / l;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Vector4::operator *= (const Matrix4 &m)
|
||||
{
|
||||
float _x = x, _y = y, _z = z;
|
||||
x = _x * m.m[0][0] + _y * m.m[0][1] + _z * m.m[0][2] + m.m[0][3];
|
||||
y = _x * m.m[1][0] + _y * m.m[1][1] + _z * m.m[1][2] + m.m[1][3];
|
||||
z = _x * m.m[2][0] + _y * m.m[2][1] + _z * m.m[2][2] + m.m[2][3];
|
||||
}
|
||||
Vector4 Vector4::operator * (const Matrix4 &m) const
|
||||
{
|
||||
return Vector4( x * m.m[0][0] + y * m.m[0][1] + z * m.m[0][2] + m.m[0][3],
|
||||
x * m.m[1][0] + y * m.m[1][1] + z * m.m[1][2] + m.m[1][3],
|
||||
x * m.m[2][0] + y * m.m[2][1] + z * m.m[2][2] + m.m[2][3] );
|
||||
}
|
||||
void Vector4::operator *= (const Matrix3 &m)
|
||||
{
|
||||
float _x = x, _y = y, _z = z;
|
||||
x = _x * m.m[0][0] + _y * m.m[0][1] + _z * m.m[0][2];
|
||||
y = _x * m.m[1][0] + _y * m.m[1][1] + _z * m.m[1][2];
|
||||
z = _x * m.m[2][0] + _y * m.m[2][1] + _z * m.m[2][2];
|
||||
}
|
||||
Vector4 Vector4::operator * (const Matrix3 &m) const
|
||||
{
|
||||
return Vector4( x * m.m[0][0] + y * m.m[0][1] + z * m.m[0][2],
|
||||
x * m.m[1][0] + y * m.m[1][1] + z * m.m[1][2],
|
||||
x * m.m[2][0] + y * m.m[2][1] + z * m.m[2][2] );
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Vector4 Vector4::FaceForward(Vector4 &dir)
|
||||
{
|
||||
if (Dot(dir) >= 0)
|
||||
return Reversed();
|
||||
return *this;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
int Vector4::Hash() const
|
||||
{
|
||||
int a = (int)(x * 10.f), b = (int)(y * 10.f), c = (int)(z * 10.f);
|
||||
// From Christer Ericson's Realtime Collision Detection.
|
||||
return a * 0x8da6b343 + b * 0xd8163841 + c * 0xcb1ab31f;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Vector4::BaseToEuler(Vector4 &euler, Vector4 &u, Vector4 *v)
|
||||
{
|
||||
float _v = Math::Sqrt(u.x * u.x + u.y * u.y + u.z * u.z);
|
||||
euler.x = -Math::ASin(u.y / _v);
|
||||
|
||||
_v = Math::Sqrt(u.x * u.x + u.z * u.z);
|
||||
if (_v > 0.00001f)
|
||||
euler.y = Math::ASin(u.x / _v);
|
||||
else euler.y = 0;
|
||||
|
||||
if (u.z < 0.f)
|
||||
{
|
||||
if (euler.y < 0.f)
|
||||
euler.y = - (Math::Pi + euler.y);
|
||||
else euler.y = Math::Pi - euler.y;
|
||||
}
|
||||
euler.z = 0;
|
||||
|
||||
if (v)
|
||||
{
|
||||
Matrix3 mx(Matrix3::RotationMatrixXAxis(Units::Rad(euler.x)));
|
||||
Matrix3 my(Matrix3::RotationMatrixYAxis(Units::Rad(euler.y)));
|
||||
Vector4 bv(Vector4(1,0,0) * my * mx), vn(v->Normalized());
|
||||
const float vc = vn.Dot(bv);
|
||||
|
||||
if (vc >= 1.f)
|
||||
euler.z = 0.f;
|
||||
else if (vc <= -1.f)
|
||||
euler.z = Math::Pi;
|
||||
else euler.z = Math::ACos(vc);
|
||||
|
||||
if ((bv.Cross(vn)).Dot(u) <= 0.f)
|
||||
euler.z = (Math::Pi + Math::Pi) - euler.z;
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Vector4 Vector4::Random(float min, float max)
|
||||
{ return Vector4(Random::FRRand(min, max), Random::FRRand(min, max), Random::FRRand(min, max)); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
NML::Tag *Vector4::AsMetaTag(const char *id, bool fulldump) const
|
||||
{
|
||||
NML::Tag *root = new NML::Tag(id ? id : "Vector");
|
||||
|
||||
if (root)
|
||||
{
|
||||
root->AddChild("X", x);
|
||||
root->AddChild("Y", y);
|
||||
root->AddChild("Z", z);
|
||||
if (fulldump)
|
||||
root->AddChild("W", w);
|
||||
}
|
||||
return root;
|
||||
}
|
||||
bool Vector4::FromMetaTag(NML::Tag &tag)
|
||||
{
|
||||
NML::Tag *t;
|
||||
List <NML::Tag *> ::Iterator i(tag.GetTags().GetRoot());
|
||||
|
||||
t = i.ObjectPtr();
|
||||
if (!t) return false;
|
||||
x = t->GetReal();
|
||||
++i;
|
||||
|
||||
t = i.ObjectPtr();
|
||||
if (!t) return false;
|
||||
y = t->GetReal();
|
||||
++i;
|
||||
|
||||
t = i.ObjectPtr();
|
||||
if (!t) return false;
|
||||
z = t->GetReal();
|
||||
++i;
|
||||
|
||||
t = i.ObjectPtr();
|
||||
if (t)
|
||||
w = t->GetReal();
|
||||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
0
include/framework/metafile/nml_binary_load.cpp
Normal file
0
include/framework/metafile/nml_binary_load.cpp
Normal file
111
include/framework/metafile/nml_binary_save.cpp
Normal file
111
include/framework/metafile/nml_binary_save.cpp
Normal file
@ -0,0 +1,111 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include <cstring>
|
||||
#include "metafile/nml.h"
|
||||
#include "filesystem/io_handle.h"
|
||||
#include "filesystem/filesystem.h"
|
||||
#include "platform.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::NML;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Parser::SaveBinaryTag(IO::Handle &out, const Tag &tag, File::Binary method)
|
||||
{
|
||||
const Variant &v = tag.GetValue();
|
||||
|
||||
if (v.GetType() != Variant::VariantNone)
|
||||
{
|
||||
out.Write <ushort> ((ushort)tag.name.Len());
|
||||
out.Write((void *)tag.name.c_str(), tag.name.Len());
|
||||
}
|
||||
else
|
||||
__ERR__(__LOG_W__ << "Ignoring invalid metatag '" << tag.name << "'.\n", true)
|
||||
|
||||
// Store tag start size/type.
|
||||
out.Write <uchar> ((uchar)v.GetType());
|
||||
|
||||
size_t tag_length_pos = out.Tell();
|
||||
switch (v.GetType())
|
||||
{
|
||||
case Variant::VariantNone:
|
||||
case Variant::VariantBinary:
|
||||
case Variant::VariantString:
|
||||
out.Write <int> (-1);
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
switch (v.GetType())
|
||||
{
|
||||
case Variant::VariantNone:
|
||||
NMLTagForeach(child, tag)
|
||||
if (!SaveBinaryTag(out, *child, method))
|
||||
return false;
|
||||
break;
|
||||
|
||||
case Variant::VariantBinary:
|
||||
out.Write(tag.GetValue().GetBinaryBuffer(), tag.GetValue().GetBinarySize());
|
||||
break;
|
||||
|
||||
case Variant::VariantInteger:
|
||||
out.Write <int> (tag.GetInteger());
|
||||
break;
|
||||
|
||||
case Variant::VariantFloat:
|
||||
out.Write <float> (tag.GetReal());
|
||||
break;
|
||||
|
||||
case Variant::VariantString:
|
||||
out.Write(tag.GetString(), std::strlen(tag.GetString()));
|
||||
break;
|
||||
|
||||
default:
|
||||
__ERR__(__LOG_E__ << "No method to output tag '" << tag.name << "' type.\n", false)
|
||||
}
|
||||
|
||||
switch (v.GetType())
|
||||
{
|
||||
case Variant::VariantNone:
|
||||
case Variant::VariantBinary:
|
||||
case Variant::VariantString:
|
||||
{
|
||||
size_t tag_end_pos = out.Tell();
|
||||
out.Seek(tag_length_pos, IO::Base::SeekStart);
|
||||
out.Write <int> (tag_end_pos - tag_length_pos);
|
||||
out.Seek(tag_end_pos, IO::Base::SeekStart);
|
||||
}
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool Parser::SaveBinary(IO::Handle &h, const File &file)
|
||||
{
|
||||
h.Write((const void *)"<BML=1.0>\n", 10);
|
||||
NMLFileForeach(tag, file)
|
||||
if (!SaveBinaryTag(h, *tag, file.GetBinaryMethod()))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
bool Parser::SaveBinary(const char *uri, const File &file)
|
||||
{
|
||||
if (!uri)
|
||||
return false;
|
||||
|
||||
AutoPtr <IO::Handle> handle;
|
||||
if (!(handle = Platform::Get().io->Open(uri, IO::ModeWrite)))
|
||||
__ERR__(__LOG_E__ << "Failed to open metafile output '" << uri << "'.\n", false)
|
||||
|
||||
return SaveBinary(*handle, file);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
83
include/framework/metafile/nml_file.cpp
Normal file
83
include/framework/metafile/nml_file.cpp
Normal file
@ -0,0 +1,83 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "metafile/nml.h"
|
||||
#include "alloc/ialloc.h"
|
||||
|
||||
using namespace GS::NML;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool File::GetBool(const char *path, bool dflt, bool verbose) const {
|
||||
Tag *t = GetTypedTag(path, Variant::VariantBool, verbose);
|
||||
return t ? t->GetBool() : dflt;
|
||||
}
|
||||
|
||||
int File::GetInteger(const char *path, int dflt, bool verbose) const {
|
||||
Tag *t = GetTypedTag(path, Variant::VariantInteger, verbose);
|
||||
return t ? t->GetInteger() : dflt;
|
||||
}
|
||||
|
||||
float File::GetReal(const char *path, float dflt, bool verbose) const {
|
||||
Tag *t = GetTypedTag(path, Variant::VariantFloat, verbose);
|
||||
return t ? t->GetReal() : dflt;
|
||||
}
|
||||
|
||||
const char *File::GetString(const char *path, const char *dflt, bool verbose) const {
|
||||
Tag *t = GetTypedTag(path, Variant::VariantString, verbose);
|
||||
return t ? t->GetString() : dflt;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void File::Import(const File &src, bool clear_before_import) {
|
||||
if (clear_before_import)
|
||||
Clear();
|
||||
ListForeachPtr(Tag *, tag, src.GetTags())
|
||||
AddRoot(tag->Clone());
|
||||
}
|
||||
|
||||
File *File::Clone() const {
|
||||
File *clone = new File;
|
||||
if (!clone)
|
||||
return NULL;
|
||||
|
||||
if (!name.IsEmpty())
|
||||
clone->name = name.c_str();
|
||||
|
||||
clone->Import(*this);
|
||||
return clone;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Tag *File::AddRoot(Tag *t) {
|
||||
if (!t)
|
||||
return NULL;
|
||||
if (!tags.Add(t))
|
||||
return NULL;
|
||||
return t;
|
||||
}
|
||||
|
||||
Tag *File::AddRoot(const char *name) { return AddRoot(new Tag(name)); }
|
||||
Tag *File::AddRoot(const char *name, bool v) { return AddRoot(new Tag(name, v)); }
|
||||
Tag *File::AddRoot(const char *name, int v) { return AddRoot(new Tag(name, v)); }
|
||||
Tag *File::AddRoot(const char *name, float v) { return AddRoot(new Tag(name, v)); }
|
||||
Tag *File::AddRoot(const char *name, const char *s) { return AddRoot(new Tag(name, s)); }
|
||||
Tag *File::AddRoot(const char *name, void *d, size_t s) { return AddRoot(new Tag(name, d, s)); }
|
||||
bool File::UnlinkRoot(Tag *t) { return tags.Remove(t); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void File::Free() {
|
||||
ListDeleteAllPtr(Tag *, tags);
|
||||
name.Clear();
|
||||
}
|
||||
|
||||
File::~File() { Free(); }
|
||||
//------------------------------------------------------------------------------
|
||||
122
include/framework/metafile/nml_generic.cpp
Normal file
122
include/framework/metafile/nml_generic.cpp
Normal file
@ -0,0 +1,122 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "metafile/nml.h"
|
||||
#include "reflection/c_refl.h"
|
||||
#include "memory/nauto_ptr.h"
|
||||
#include "assert/nassert.h"
|
||||
|
||||
|
||||
namespace GS {
|
||||
namespace NML {
|
||||
using namespace Reflection;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool GenericObjectFromMetaTag(Tag &t, void *o, Property *o_prop)
|
||||
{
|
||||
NMLTagForeach(pt, t)
|
||||
for (int n = 0; o_prop[n].name; ++n)
|
||||
if (pt->name == o_prop[n].name)
|
||||
{
|
||||
size_t p_prop = (size_t)o + o_prop[n].offset_of;
|
||||
|
||||
switch (o_prop[n].type)
|
||||
{
|
||||
case Property::BoolProp:
|
||||
*(bool *)p_prop = pt->GetBool();
|
||||
break;
|
||||
case Property::CharProp:
|
||||
*(char *)p_prop = (char)pt->GetInteger();
|
||||
break;
|
||||
case Property::ShortProp:
|
||||
*(short *)p_prop = (short)pt->GetInteger();
|
||||
break;
|
||||
case Property::IntProp:
|
||||
*(int *)p_prop = pt->GetInteger();
|
||||
break;
|
||||
case Property::FloatProp:
|
||||
*(float *)p_prop = pt->GetReal();
|
||||
break;
|
||||
case Property::StringProp:
|
||||
*(GS::String *)p_prop = pt->GetString();
|
||||
break;
|
||||
case Property::EnumProp:
|
||||
__ASSERT__(o_prop[n].enum_dict);
|
||||
*(int *)p_prop = Enum::fromString(pt->GetString(), o_prop[n].enum_dict);
|
||||
break;
|
||||
|
||||
default:
|
||||
__ASSERT_ALWAYS__;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
Tag *GenericObjectToMetaTag(Tag *t, const void *o, Property *o_prop)
|
||||
{
|
||||
if (t)
|
||||
for (int n = 0; o_prop[n].name; ++n)
|
||||
{
|
||||
size_t p_prop = (size_t)o + o_prop[n].offset_of;
|
||||
|
||||
switch (o_prop[n].type)
|
||||
{
|
||||
case Property::BoolProp:
|
||||
t->AddChild(o_prop[n].name, *(bool *)p_prop);
|
||||
break;
|
||||
case Property::CharProp:
|
||||
t->AddChild(o_prop[n].name, (int)*(char *)p_prop);
|
||||
break;
|
||||
case Property::ShortProp:
|
||||
t->AddChild(o_prop[n].name, (int)*(short *)p_prop);
|
||||
break;
|
||||
case Property::IntProp:
|
||||
t->AddChild(o_prop[n].name, *(int *)p_prop);
|
||||
break;
|
||||
case Property::FloatProp:
|
||||
t->AddChild(o_prop[n].name, *(float *)p_prop);
|
||||
break;
|
||||
case Property::StringProp:
|
||||
t->AddChild(o_prop[n].name, ((GS::String *)p_prop)->c_str());
|
||||
break;
|
||||
case Property::EnumProp:
|
||||
__ASSERT__(o_prop[n].enum_dict);
|
||||
t->AddChild(o_prop[n].name, Enum::toString(*(int *)p_prop, o_prop[n].enum_dict));
|
||||
break;
|
||||
|
||||
default:
|
||||
__ASSERT_ALWAYS__;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return t;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool GenericObjectFromMetaFile(const char *uri, void *obj, Property *obj_prop, const char *root_name)
|
||||
{
|
||||
File file;
|
||||
if (!Parser::Load(uri, file))
|
||||
return false;
|
||||
|
||||
if (Tag *root = file.GetTag(root_name))
|
||||
if (!GenericObjectFromMetaTag(*root, obj, obj_prop))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
bool GenericObjectToMetaFile(const char *uri, const void *obj, Property *obj_prop, const char *root_name)
|
||||
{
|
||||
File file;
|
||||
file.AddRoot(GenericObjectToMetaTag(new Tag(root_name), obj, obj_prop));
|
||||
return Parser::Save(uri, file);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} //NML
|
||||
} // GS
|
||||
380
include/framework/metafile/nml_load.cpp
Normal file
380
include/framework/metafile/nml_load.cpp
Normal file
@ -0,0 +1,380 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include <cstring>
|
||||
#include "metafile/nml.h"
|
||||
#include "ascii/parser.h"
|
||||
#include "ascii/ascii_encoder.h"
|
||||
#include "filesystem/io_handle.h"
|
||||
#include "filesystem/filesystem.h"
|
||||
#include "platform.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::NML;
|
||||
using namespace GS::AsciiParser;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Parser::IsMetafile(const char *name)
|
||||
{
|
||||
AutoPtr <IO::Handle> h(Platform::Get().io->Open(name));
|
||||
if (h.IsNull())
|
||||
return false;
|
||||
|
||||
char header[9];
|
||||
h->Read(header, 9);
|
||||
if (Memory::Compare(header, "<Version=", 9) && Memory::Compare(header, "<NML=", 5))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
const char *Parser::ParseTagPreprocessorDirective(Tag &tag, const char *s, const char *e)
|
||||
{
|
||||
s++;
|
||||
|
||||
// Catch preprocessor directive.
|
||||
if (!strncmp(s, "include", 7))
|
||||
{
|
||||
// Ensure base coherency.
|
||||
if (tag.GetValue().GetType() != Variant::VariantNone)
|
||||
__ERR__(__LOG_E__ << "Incoherent type in tag '" << tag.name << "' declaration.\n", NULL)
|
||||
|
||||
// Parse directive.
|
||||
s += SkipSpace(s + 7, e) - s;
|
||||
if (s[0] != '(')
|
||||
__ERR__(__LOG_E__ << "Mangled #include directive.\n", NULL);
|
||||
|
||||
// Fetch arguments.
|
||||
String include_path;
|
||||
|
||||
forever
|
||||
{
|
||||
if (s[0] == ')')
|
||||
break;
|
||||
s += SkipSpace(s + 1, e) - s;
|
||||
|
||||
// Path.
|
||||
if (s[0] == '"')
|
||||
{
|
||||
s++;
|
||||
uint len = (uint)(RunToEOS(s, e) - s);
|
||||
include_path.Set(s, s + len);
|
||||
|
||||
s += SkipSpace(s + len + 1, e) - s;
|
||||
}
|
||||
else
|
||||
__ERR__(__LOG_E__ << "Unexpected character in #include directive.\n", NULL);
|
||||
}
|
||||
s++;
|
||||
|
||||
// Load external metafile.
|
||||
File tmp_file;
|
||||
|
||||
if (Parser::Load(include_path, tmp_file, true))
|
||||
{
|
||||
// Then transfer all tags to our current file.
|
||||
NMLFileForeach(t, tmp_file)
|
||||
{
|
||||
if (tmp_file.UnlinkRoot(t))
|
||||
tag.AddChild(t);
|
||||
else
|
||||
__LOG_E__ << "Could not relink root tag from included metafile.\n";
|
||||
}
|
||||
}
|
||||
else
|
||||
__LOG_E__ << "Failed to include external metafile '" << include_path << "'.\n";
|
||||
}
|
||||
else if (!strncmp(s, "append", 6))
|
||||
__LOG_E__ << "Preprocessor directive #append deprecated.\n";
|
||||
else
|
||||
__ERR__(__LOG_E__ << "Unknown preprocessor directive, metatag '" << tag.name << "'.\n", NULL)
|
||||
|
||||
return s;
|
||||
}
|
||||
bool Parser::ParseTag(Tag &tag, const char *s, const char *e, const char **es)
|
||||
{
|
||||
// Safety net.
|
||||
s += SkipSpace(s, e) - s;
|
||||
if (s[0] != '<')
|
||||
return false;
|
||||
|
||||
// Get the tag id.
|
||||
#define MLTAG_ERROR(c) { __LOG_E__ << c; return false; }
|
||||
|
||||
const char *etn = s + 1;
|
||||
etn += SkipEntry(etn, e) - etn;
|
||||
if (String::strfindchar(s + 1, ':', etn) != etn)
|
||||
MLTAG_ERROR("':' is a path character and cannot be used in tag name.\n")
|
||||
if (String::strfindchar(s + 1, ';', etn) != etn)
|
||||
MLTAG_ERROR("';' is a path character and cannot be used in tag name.\n")
|
||||
|
||||
tag.name.Set(s + 1, etn);
|
||||
s = etn;
|
||||
|
||||
// Get tag type.
|
||||
s += SkipSpace(s, e) - s;
|
||||
if (s == e)
|
||||
MLTAG_ERROR("Mangled definition, metatag '" << tag.name << "'.\n")
|
||||
|
||||
switch (s[0])
|
||||
{
|
||||
case '>':
|
||||
s++;
|
||||
break;
|
||||
|
||||
// Node/real/integer/string.
|
||||
case '=':
|
||||
{
|
||||
s++;
|
||||
forever
|
||||
{
|
||||
s += SkipSpace(s, e) - s;
|
||||
if (s == e)
|
||||
MLTAG_ERROR("Mangled definition, metatag '" << tag.name << "'.\n")
|
||||
|
||||
// End of tag.
|
||||
if (s[0] == '>')
|
||||
{
|
||||
s++;
|
||||
break;
|
||||
}
|
||||
|
||||
// Preprocessor directive.
|
||||
if (s[0] == '#')
|
||||
s = ParseTagPreprocessorDirective(tag, s + 1, e);
|
||||
|
||||
// Node.
|
||||
else if (s[0] == '<')
|
||||
{
|
||||
if (tag.GetValue().GetType() != Variant::VariantNone)
|
||||
MLTAG_ERROR("Incoherent type in tag '" << tag.name << "' declaration.\n")
|
||||
|
||||
Tag *stag = tag.tags.Add(new Tag)->Object();
|
||||
if (!ParseTag(*stag, s, e, &s))
|
||||
MLTAG_ERROR("")
|
||||
s += SkipSpace(s, e) - s;
|
||||
}
|
||||
|
||||
// Constant.
|
||||
else
|
||||
{
|
||||
if (tag.GetValue().GetType() != Variant::VariantNone)
|
||||
MLTAG_ERROR("Incoherent type in tag '" << tag.name << "' declaration.\n")
|
||||
|
||||
// Binary.
|
||||
if (s[0] == '=')
|
||||
{
|
||||
s++;
|
||||
if (!(s[0] >= '0' && s[0] <= '9'))
|
||||
MLTAG_ERROR("Expected encoded size in binary tag '" << tag.name << "' declaration.\n")
|
||||
|
||||
const char *ye = s;
|
||||
while (ye[0] >= '0' && ye[0] <= '9')
|
||||
ye++;
|
||||
if (ye[0] != ':')
|
||||
MLTAG_ERROR("Expected size delimiter in binary tag '" << tag.name << "' declaration.\n")
|
||||
uint asize = String(s, ye).Integer();
|
||||
|
||||
s = ye + 1;
|
||||
if (!(s[0] >= '0' && s[0] <= '9'))
|
||||
MLTAG_ERROR("Expected binary size in binary tag '" << tag.name << "' declaration.\n")
|
||||
ye = s;
|
||||
while (ye[0] >= '0' && ye[0] <= '9')
|
||||
ye++;
|
||||
|
||||
// Trailing @ means yEnc binary.
|
||||
File::Binary encoding = File::Binary_UU;
|
||||
if (ye[0] == '@')
|
||||
{
|
||||
encoding = File::Binary_yEnc;
|
||||
ye++;
|
||||
}
|
||||
|
||||
// Detect EOL
|
||||
if ((ye[0] != 0x0a) && ((ye[0] != 0x0d) && (ye[1] != 0x0a)))
|
||||
MLTAG_ERROR("Expected EOL following binary size in binary tag '" << tag.name << "' declaration.\n")
|
||||
|
||||
size_t eol_size = (ye[0] == 0x0a) ? 1 : 2;
|
||||
uint bsize = String(s, ye).Integer();
|
||||
|
||||
uchar *astart = (uchar *)(ye + eol_size);
|
||||
|
||||
// [EJ] Adjust asize to account for Windows EOL (historically NML only specifies Unix ascii size).
|
||||
if (eol_size > 1)
|
||||
{
|
||||
__LOG_V__ << "CRLF reduces NML binary load performance.\n";
|
||||
|
||||
size_t a_size_in = asize;
|
||||
asize = 0;
|
||||
|
||||
for (; a_size_in > 0; --a_size_in)
|
||||
if ((astart[asize] == 0x0d) && (astart[asize + 1] == 0x0a))
|
||||
asize += 2;
|
||||
else
|
||||
++asize;
|
||||
}
|
||||
// Load ASCII encoded data.
|
||||
Array <uchar> aenc(asize, Alloc::Metatag);
|
||||
if (!aenc)
|
||||
MLTAG_ERROR("Failed to allocate binary buffer in binary tag '" << tag.name << "'.\n")
|
||||
|
||||
memcpy(&aenc[0], astart, asize);
|
||||
|
||||
s = ye + eol_size + asize;
|
||||
if (s[0] != '>')
|
||||
MLTAG_ERROR("Expected closing tag in tag '" << tag.name << "'.\n")
|
||||
|
||||
Array <uchar> data(bsize, Alloc::Metatag);
|
||||
if (data)
|
||||
{
|
||||
switch (encoding)
|
||||
{
|
||||
case File::Binary_UU: nAsciiEncoder::UUDecode(&aenc[0], asize, &data[0], bsize); break;
|
||||
case File::Binary_yEnc: nAsciiEncoder::yDecode(&aenc[0], asize, &data[0], bsize); break;
|
||||
}
|
||||
tag.GetValue().SetBinary(&data[0], bsize);
|
||||
}
|
||||
}
|
||||
// Real/Integer.
|
||||
else if ((s[0] >= '0' && s[0] <= '9') || (s[0] == '.') || (s[0] == '-'))
|
||||
{
|
||||
if (IsConstantFloat(s, e))
|
||||
tag.GetValue() = String::atof(s, e, true);
|
||||
else tag.GetValue() = String::atoi(s);
|
||||
|
||||
if (s[0] == '-')
|
||||
s++;
|
||||
s += SkipEntry(s, e) - s;
|
||||
|
||||
if (s[0] != '>')
|
||||
MLTAG_ERROR("Unexpected trailing expression following value, metatag '" << tag.name << "'.\n")
|
||||
}
|
||||
// String.
|
||||
else if (s[0] == '\"')
|
||||
{
|
||||
s++;
|
||||
ptrdiff_t len = RunToEOS(s, e) - s;
|
||||
if ((s + len) == e)
|
||||
MLTAG_ERROR("Mangled string declaration, metatag '" << tag.name << "'.\n")
|
||||
|
||||
// Copy string.
|
||||
tag.GetValue() = String(s, s + len);
|
||||
|
||||
s += SkipSpace(s + len + 1, e) - s; // Jump over string.
|
||||
if (s == e)
|
||||
MLTAG_ERROR("Unexpected EOF after string declaration, metatag '" << tag.name << "'.\n")
|
||||
if (s[0] != '>')
|
||||
MLTAG_ERROR("Unexpected trailing expression following string object, metatag '" << tag.name << "'.\n")
|
||||
|
||||
tag.GetValue().s_value.ReplaceAll("\\n", "\n"); // convert CF
|
||||
}
|
||||
// Boolean.
|
||||
else if (!strncmp(s, "True", 4))
|
||||
{
|
||||
tag.GetValue() = true;
|
||||
s += SkipSpace(s + 4, e) - s;
|
||||
if (s[0] != '>')
|
||||
MLTAG_ERROR("Unexpected trailing expression following value, metatag '" << tag.name << "'.\n")
|
||||
}
|
||||
else if (!strncmp(s, "False", 5))
|
||||
{
|
||||
tag.GetValue() = false;
|
||||
s += SkipSpace(s + 5, e) - s;
|
||||
if (s[0] != '>')
|
||||
MLTAG_ERROR("Unexpected trailing expression following value, metatag '" << tag.name << "'.\n")
|
||||
}
|
||||
else
|
||||
MLTAG_ERROR("Unexpected '" << s[0] << "' in assignation, metatag '" << tag.name << "'.\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
MLTAG_ERROR("Unexpected trailing expression after metatag '" << tag.name << "' name declaration.\n")
|
||||
}
|
||||
if (es)
|
||||
es[0] = s;
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Parser::LoadFromMemory(const char *data, size_t size, File &mfl)
|
||||
{
|
||||
mfl.Free();
|
||||
if (!data)
|
||||
return false;
|
||||
|
||||
// Read header tag, expected to be NML version.
|
||||
const char *pof = data, *eof = data + size;
|
||||
|
||||
#define MEM_MLP_ERROR(c) { (c); return false; }
|
||||
|
||||
Tag header_tag;
|
||||
if (!ParseTag(header_tag, pof, eof, &pof))
|
||||
MEM_MLP_ERROR(__LOG_E__ << "Invalid metafile.\n")
|
||||
if ((header_tag.name != "Version") && (header_tag.name != "NML"))
|
||||
MEM_MLP_ERROR(__LOG_E__ << "Unknown metafile variant.\n")
|
||||
|
||||
switch (header_tag.GetValue().GetType())
|
||||
{
|
||||
case Variant::VariantInteger:
|
||||
if (header_tag.GetInteger() > version)
|
||||
__LOG_W__ << "Newer version NML header found (" << header_tag.GetInteger() << ">" << version << ").\n";
|
||||
break;
|
||||
|
||||
case Variant::VariantFloat:
|
||||
if (header_tag.GetReal() > version)
|
||||
__LOG_W__ << "Newer version NML header found (" << header_tag.GetReal() << ">" << version << ").\n";
|
||||
break;
|
||||
|
||||
default:
|
||||
__LOG_W__ << "Unknown NML header version identification method.\n";
|
||||
break;
|
||||
}
|
||||
|
||||
// Read all root tags.
|
||||
while (pof < eof)
|
||||
{
|
||||
Tag *tag = mfl.tags.Add(new Tag)->Object();
|
||||
if (!ParseTag(*tag, pof, eof, &pof))
|
||||
return false;
|
||||
pof += SkipSpace(pof, eof) - pof;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool Parser::Load(const char *path, File &file, bool verbose)
|
||||
{
|
||||
if (!path)
|
||||
return false;
|
||||
|
||||
Array <char> data;
|
||||
if (!Platform::Get().io->FileLoad(path, data, verbose))
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
if (!LoadFromMemory(data.c_ptr(), data.GetSize(), file))
|
||||
return false;
|
||||
}
|
||||
catch (char *e)
|
||||
{
|
||||
__LOG_E__ << "Failed to load file LoadFromMemory. " << path << "\n";
|
||||
return false;
|
||||
}
|
||||
file.name = path;
|
||||
return true;
|
||||
}
|
||||
File *Parser::Load(const char *metafile, bool verbose)
|
||||
{
|
||||
AutoPtr <File> mfl(new File);
|
||||
if (mfl.IsNull())
|
||||
__ERR__(__LOG_E__ << "Failed to allocate file.\n", NULL)
|
||||
return Load(metafile, *mfl, verbose) ? mfl.Detach() : NULL;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
97
include/framework/metafile/nml_query.cpp
Normal file
97
include/framework/metafile/nml_query.cpp
Normal file
@ -0,0 +1,97 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "metafile/nml.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::NML;
|
||||
|
||||
|
||||
static bool MetatagNameCompare(const Tag *o, const String &name) { return o->name == name; }
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Tag *Tag::GetTagEx(const List <Tag *> &tg, const char *s, const File *, bool verbose)
|
||||
{
|
||||
if (!s)
|
||||
return NULL;
|
||||
|
||||
const char *path = s;
|
||||
const List <Tag *> *list = &tg;
|
||||
|
||||
Tag *tag = NULL;
|
||||
|
||||
while (s[0])
|
||||
{
|
||||
while (s[0] == ':')
|
||||
s++;
|
||||
if (s[0] == ';')
|
||||
break;
|
||||
|
||||
const char *t = s;
|
||||
while ((t[0] != ';') && (t[0] != ':') && t[0])
|
||||
t++;
|
||||
if (!t[0] && verbose)
|
||||
{
|
||||
if (t > s)
|
||||
__LOG_W__ << "incomplete path '" << path << "' (missing ';').\n";
|
||||
else __LOG_E__ << "unexpected end of path'" << path << "'.\n";
|
||||
}
|
||||
|
||||
// No more node to search.
|
||||
if (!list)
|
||||
{
|
||||
if (verbose)
|
||||
__LOG_W__ << "'" << path << "' is deeper than lowest tree node.\n";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
String node_name(s, t);
|
||||
s = t;
|
||||
tag = ListFindEx(*list, MetatagNameCompare, node_name);
|
||||
|
||||
if (tag == NULL)
|
||||
{
|
||||
if (verbose)
|
||||
__LOG__ << "!! Error '" << node_name << "' in '" << path << "' not found.\n";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (tag->GetValue().GetType())
|
||||
{
|
||||
case Variant::VariantNone:
|
||||
list = &tag->tags;
|
||||
break;
|
||||
|
||||
default:
|
||||
list = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return tag;
|
||||
}
|
||||
Tag *Tag::GetTag(const char *path, const File *root, bool verbose) const
|
||||
{
|
||||
return GetTagEx(tags, path, root, verbose);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Tag *Tag::GetTypedTag(const char *path, Variant::Type type, const File *root, bool verbose) const
|
||||
{
|
||||
Tag *t = Tag::GetTagEx(tags, path, root, verbose);
|
||||
if ((!t) || (t->GetValue().GetType() != type))
|
||||
return NULL;
|
||||
return t;
|
||||
}
|
||||
Tag *File::GetTypedTag(const char *path, Variant::Type type, bool verbose) const
|
||||
{
|
||||
Tag *t = Tag::GetTagEx(tags, path, this, verbose);
|
||||
if ((!t) || (t->GetValue().GetType() != type))
|
||||
return NULL;
|
||||
return t;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
152
include/framework/metafile/nml_save.cpp
Normal file
152
include/framework/metafile/nml_save.cpp
Normal file
@ -0,0 +1,152 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include "metafile/nml.h"
|
||||
#include "ascii/ascii_encoder.h"
|
||||
#include "filesystem/filesystem.h"
|
||||
#include "filesystem/io_handle.h"
|
||||
#include "memory/nauto_ptr.h"
|
||||
#include "platform_config.h"
|
||||
#include "platform.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::NML;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Parser::SaveTag(IO::Handle &out, const Tag &tag, File::Binary method, uint idt)
|
||||
{
|
||||
//---------------------------------------------------------------------------
|
||||
#define OUTPUT_INDENT { for (uint n = 0; n < idt; n++) out << "\t"; }
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
if (tag.name.IsEmpty() && !tag.GetChildCount())
|
||||
return true; // silently skip this tag
|
||||
|
||||
OUTPUT_INDENT;
|
||||
out << "<" << tag.name.c_str();
|
||||
|
||||
switch (tag.GetValue().GetType())
|
||||
{
|
||||
case Variant::VariantNone:
|
||||
if (tag.GetChildCount())
|
||||
{
|
||||
out << "=\n";
|
||||
|
||||
NMLTagForeach(child, tag)
|
||||
if (!SaveTag(out, *child, method, idt + 1))
|
||||
return false;
|
||||
|
||||
OUTPUT_INDENT;
|
||||
}
|
||||
break;
|
||||
|
||||
case Variant::VariantBinary:
|
||||
{
|
||||
uint olen = (uint)~0;
|
||||
|
||||
switch (method)
|
||||
{
|
||||
case File::Binary_UU:
|
||||
olen = nAsciiEncoder::UUEncode((uchar *)tag.GetValue().GetBinaryBuffer(), tag.GetValue().GetBinarySize());
|
||||
break;
|
||||
|
||||
case File::Binary_yEnc:
|
||||
olen = nAsciiEncoder::yEncode((uchar *)tag.GetValue().GetBinaryBuffer(), tag.GetValue().GetBinarySize());
|
||||
break;
|
||||
}
|
||||
|
||||
if (olen)
|
||||
{
|
||||
Array <uchar> aenc(olen, Alloc::Metatag);
|
||||
|
||||
if (aenc.IsValid())
|
||||
{
|
||||
uint asize = 0;
|
||||
switch (method)
|
||||
{
|
||||
case File::Binary_UU:
|
||||
asize = nAsciiEncoder::UUEncode((uchar *)tag.GetValue().GetBinaryBuffer(), tag.GetValue().GetBinarySize(), &aenc[0], olen);
|
||||
break;
|
||||
|
||||
case File::Binary_yEnc:
|
||||
asize = nAsciiEncoder::yEncode((uchar *)tag.GetValue().GetBinaryBuffer(), tag.GetValue().GetBinarySize(), &aenc[0], olen);
|
||||
break;
|
||||
}
|
||||
if (asize != olen)
|
||||
__LOG_W__ << "Internal ASCII encoding inconsistency detected while processing tag '" << tag.name << "'.\n";
|
||||
|
||||
char str[256];
|
||||
_snprintf(str, 255, "==%d:%d", asize, tag.GetValue().GetBinarySize()); // ==[encoded size:decoded size] is encoded binary.
|
||||
out << str;
|
||||
|
||||
if (method == File::Binary_yEnc) // @ marker select yEncoding.
|
||||
out << "@";
|
||||
out << "\n";
|
||||
|
||||
out.Write(aenc, asize);
|
||||
}
|
||||
else
|
||||
__LOG_E__ << "Tag '" << tag.name << "' failed to allocate internal binary buffer.\n";
|
||||
}
|
||||
// else __LOG_W__ << "NULL size ASCII encoded binary tag '" << tag.id << "'.\n";
|
||||
}
|
||||
break;
|
||||
|
||||
case Variant::VariantInteger:
|
||||
{
|
||||
char str[256];
|
||||
_snprintf(str, 255, "=%d", tag.GetInteger());
|
||||
out << str;
|
||||
}
|
||||
break;
|
||||
|
||||
case Variant::VariantFloat:
|
||||
{
|
||||
char str[256];
|
||||
_snprintf(str, 255, "=%f", tag.GetReal());
|
||||
out << str;
|
||||
}
|
||||
break;
|
||||
|
||||
case Variant::VariantString:
|
||||
out << "=\"" << tag.GetString() << "\"";
|
||||
break;
|
||||
|
||||
case Variant::VariantBool:
|
||||
out << "=" << (tag.GetBool() ? "True" : "False");
|
||||
break;
|
||||
|
||||
default:
|
||||
__ERR__(__LOG_E__ << "No method to output tag '" << tag.name << "' type.\n", false)
|
||||
}
|
||||
|
||||
out << ">\n";
|
||||
return true;
|
||||
}
|
||||
bool Parser::Save(IO::Handle &h, const File &file)
|
||||
{
|
||||
h << "<Version=1.0>\n";
|
||||
NMLFileForeach(tag, file)
|
||||
if (!SaveTag(h, *tag, file.GetBinaryMethod(), 0))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
bool Parser::Save(const char *uri, const File &file)
|
||||
{
|
||||
if (!uri)
|
||||
return false;
|
||||
|
||||
AutoPtr <IO::Handle> h(Platform::Get().io->Open(uri, IO::ModeWrite));
|
||||
if (h.IsNull())
|
||||
__ERR__(__LOG_E__ << "Failed to open nml output '" << uri << "'.\n", false)
|
||||
|
||||
return Save(*h, file);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
65
include/framework/metafile/nml_string.cpp
Normal file
65
include/framework/metafile/nml_string.cpp
Normal file
@ -0,0 +1,65 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "metafile/nml_string.h"
|
||||
#include "metafile/nml.h"
|
||||
#include "filesystem/io_memory.h"
|
||||
#include "memory/nauto_ptr.h"
|
||||
#include "nstring/nstring.h"
|
||||
#include "log/log.h"
|
||||
|
||||
|
||||
namespace GS {
|
||||
namespace NML {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool TagToString(const Tag &tag, String &str)
|
||||
{
|
||||
IO::Memory memory_fs;
|
||||
|
||||
AutoPtr <IO::Handle> h(memory_fs.Open("file", IO::ModeWrite));
|
||||
if (h.IsNull() || !Parser::SaveTag(*h, tag))
|
||||
return false;
|
||||
h = NULL;
|
||||
|
||||
Array <char> data;
|
||||
if (!memory_fs.FileLoad("file", data))
|
||||
return false;
|
||||
|
||||
str.Set(data.Start(), data.End());
|
||||
return true;
|
||||
}
|
||||
bool TagFromString(const String &str, Tag &tag)
|
||||
{
|
||||
return Parser::ParseTag(tag, str.c_str(), &str.c_str()[str.Len()]);
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool FileToString(const File &file, String &str)
|
||||
{
|
||||
IO::Memory memory_fs;
|
||||
|
||||
AutoPtr <IO::Handle> h(memory_fs.Open("file", IO::ModeWrite));
|
||||
if (h.IsNull() || !Parser::Save(*h, file))
|
||||
return false;
|
||||
h = NULL;
|
||||
|
||||
Array <char> data;
|
||||
if (!memory_fs.FileLoad("file", data))
|
||||
return false;
|
||||
|
||||
str.Set(data.Start(), data.End());
|
||||
return true;
|
||||
}
|
||||
bool FileFromString(const String &str, File &file)
|
||||
{
|
||||
return Parser::LoadFromMemory(str.c_str(), str.Len(), file);
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
} // NML
|
||||
} // GS
|
||||
121
include/framework/metafile/nml_tag.cpp
Normal file
121
include/framework/metafile/nml_tag.cpp
Normal file
@ -0,0 +1,121 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "metafile/nml.h"
|
||||
#include "filesystem/io_handle.h"
|
||||
#include "alloc/ialloc.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS::NML;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Tag *Tag::GetParent(Tag *root) const
|
||||
{
|
||||
ListForeachPtr(Tag *, child, root->GetTags())
|
||||
if (child == this)
|
||||
return root;
|
||||
|
||||
Tag *parent = NULL;
|
||||
ListForeachPtr(Tag *, child, root->GetTags())
|
||||
if ((parent = GetParent(child)) != NULL)
|
||||
break;
|
||||
|
||||
return parent;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Tag *Tag::AddChild(Tag *t)
|
||||
{
|
||||
if (!t)
|
||||
return NULL;
|
||||
if (value.type != Variant::VariantNone)
|
||||
__ERR__(__LOG_E__ << "Cannot add child to tag '" << name << "' as it is neither a pure tag or a node.\n", NULL)
|
||||
if (!tags.Add(t))
|
||||
return NULL;
|
||||
return t;
|
||||
}
|
||||
Tag *Tag::AddChild(const char *name)
|
||||
{ return AddChild(new Tag(name)); }
|
||||
Tag *Tag::AddChild(const char *name, bool v)
|
||||
{ return AddChild(new Tag(name, v)); }
|
||||
Tag *Tag::AddChild(const char *name, int v)
|
||||
{ return AddChild(new Tag(name, v)); }
|
||||
Tag *Tag::AddChild(const char *name, uint v)
|
||||
{ return AddChild(new Tag(name, v)); }
|
||||
Tag *Tag::AddChild(const char *name, float v)
|
||||
{ return AddChild(new Tag(name, v)); }
|
||||
Tag *Tag::AddChild(const char *name, const char *s)
|
||||
{ return AddChild(new Tag(name, s)); }
|
||||
Tag *Tag::AddChild(const char *name, void *buffer, size_t size)
|
||||
{ return AddChild(new Tag(name, buffer, size)); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Tag::Clone(const Tag &src, bool recursive)
|
||||
{
|
||||
Free();
|
||||
|
||||
value = src.GetValue();
|
||||
|
||||
if (recursive)
|
||||
ListForeachPtr(Tag *, ct, src.GetTags())
|
||||
AddChild(ct->Clone(true));
|
||||
|
||||
return true;
|
||||
}
|
||||
Tag *Tag::Clone(bool recursive) const
|
||||
{
|
||||
Tag *clone = new Tag(name);
|
||||
if (!clone)
|
||||
return NULL;
|
||||
|
||||
// Copy tag content.
|
||||
clone->GetValue() = value;
|
||||
|
||||
// Clone children.
|
||||
if (recursive)
|
||||
ListForeachPtr(Tag *, ct, tags)
|
||||
clone->AddChild(ct->Clone(true));
|
||||
|
||||
return clone;
|
||||
}
|
||||
uint Tag::DeleteChildren(const char *filter)
|
||||
{
|
||||
uint count = 0;
|
||||
|
||||
if (filter)
|
||||
{
|
||||
String _filter(filter);
|
||||
|
||||
ListForeachPtr(Tag *, t, tags)
|
||||
if (t->name == _filter)
|
||||
{
|
||||
tags.Remove(t);
|
||||
_safe_delete(t);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
else
|
||||
ListDeleteAllPtr(Tag *, tags)
|
||||
|
||||
return count;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Tag::Free()
|
||||
{
|
||||
value.Free();
|
||||
DeleteChildren();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Tag::~Tag()
|
||||
{ Free(); }
|
||||
//------------------------------------------------------------------------------
|
||||
205
include/framework/picture/pict.cpp
Normal file
205
include/framework/picture/pict.cpp
Normal file
@ -0,0 +1,205 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include "picture/pict.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Picture::HasAlpha() const
|
||||
{
|
||||
uchar *pdata = GetData();
|
||||
if (!pdata || !width || !height)
|
||||
return false;
|
||||
|
||||
for (uint y = 0; y < height; y++)
|
||||
for (uint x = 0; x < width; x++)
|
||||
{
|
||||
union
|
||||
{
|
||||
uint packed;
|
||||
uchar ppack[4];
|
||||
};
|
||||
|
||||
switch (pxformat.GetBpp())
|
||||
{
|
||||
case 8: ppack[0] = pdata[0]; pdata++; break;
|
||||
case 16: ppack[0] = pdata[0]; ppack[1] = pdata[1]; pdata += 2; break;
|
||||
case 24: ppack[0] = pdata[0]; ppack[1] = pdata[1]; ppack[2] = pdata[2]; pdata += 3; break;
|
||||
case 32: ppack[0] = pdata[0]; ppack[1] = pdata[1]; ppack[2] = pdata[2]; ppack[3] = pdata[3]; pdata += 4; break;
|
||||
}
|
||||
|
||||
int a = (int)(((packed & pxformat.desc.amask) >> pxformat.ashift) << (8 - pxformat.acount));
|
||||
if (a < ((1 << pxformat.acount) - 1))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
uint Picture::ColorBlend(uint u, uint v, float opacity)
|
||||
{
|
||||
int fk = (int)(opacity * 65536),
|
||||
ik = 65536 - fk;
|
||||
|
||||
uint ta = ((u >> 24) & 0xff) * ik + ((v >> 24) & 0xff) * fk,
|
||||
tr = ((u >> 16) & 0xff) * ik + ((v >> 16) & 0xff) * fk,
|
||||
tg = ((u >> 8) & 0xff) * ik + ((v >> 8) & 0xff) * fk,
|
||||
tb = (u & 0xff) * ik + (v & 0xff) * fk;
|
||||
|
||||
return ((ta << 8) & 0xff000000) + (tr & 0xff0000) + ((tg >> 8) & 0xff00) + (tb >> 16);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Picture::Compare(const Picture &picture) const
|
||||
{
|
||||
if (pxformat != picture.GetPixelFormat().GetDesc())
|
||||
return false;
|
||||
// Binary comparison.
|
||||
return !Memory::Compare(data, picture.GetData(), width * height * (pxformat.GetBpp() >> 3));
|
||||
}
|
||||
bool Picture::ComputeHash()
|
||||
{
|
||||
uchar *pdata = GetData();
|
||||
if (!pdata || !width || !height)
|
||||
return false;
|
||||
|
||||
hash = 0;
|
||||
for (uint y = 0; y < height; y++)
|
||||
for (uint x = 0; x < width; x++)
|
||||
{
|
||||
union
|
||||
{
|
||||
uint packed;
|
||||
uchar ppack[4];
|
||||
};
|
||||
|
||||
switch (pxformat.GetBpp())
|
||||
{
|
||||
case 8: ppack[0] = pdata[0]; pdata++; break;
|
||||
case 16: ppack[0] = pdata[0]; ppack[1] = pdata[1]; pdata += 2; break;
|
||||
case 24: ppack[0] = pdata[0]; ppack[1] = pdata[1]; ppack[2] = pdata[2]; pdata += 3; break;
|
||||
case 32: ppack[0] = pdata[0]; ppack[1] = pdata[1]; ppack[2] = pdata[2]; ppack[3] = pdata[3]; pdata += 4; break;
|
||||
}
|
||||
|
||||
int a = (int)(((packed & pxformat.desc.amask) >> pxformat.ashift) << (8 - pxformat.acount));
|
||||
int r = (int)(((packed & pxformat.desc.rmask) >> pxformat.rshift) << (8 - pxformat.rcount));
|
||||
int g = (int)(((packed & pxformat.desc.gmask) >> pxformat.gshift) << (8 - pxformat.gcount));
|
||||
int b = (int)(((packed & pxformat.desc.bmask) >> pxformat.bshift) << (8 - pxformat.bcount));
|
||||
|
||||
hash += (a << 24) + (r << 16) + (g << 8) + b;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Picture::SetData(void *ud, uint w, uint h, const PixelFormatDescription &fmt, bool take_ownership)
|
||||
{
|
||||
if (!ud)
|
||||
return;
|
||||
|
||||
FreeData();
|
||||
hash = 0;
|
||||
|
||||
data = (uchar *)ud;
|
||||
if (!take_ownership)
|
||||
pic_flag.Set(HasForeignData);
|
||||
|
||||
width = w;
|
||||
height = h;
|
||||
pxformat.Set(fmt);
|
||||
}
|
||||
bool Picture::AllocAs(uint w, uint h, const PixelFormatDescription &fmt)
|
||||
{
|
||||
if (!pic_flag.IsSet(HasForeignData) && data && (width == w) && (height == h) && (pxformat.GetBpp() == fmt.bpp))
|
||||
{
|
||||
pxformat.Set(fmt);
|
||||
return true;
|
||||
}
|
||||
|
||||
FreeData();
|
||||
|
||||
if (w && h && fmt.bpp)
|
||||
{
|
||||
size_t count = w * h * (fmt.bpp / 8);
|
||||
|
||||
data = AllocMemory(count);
|
||||
if (data)
|
||||
memset(data, 0, sizeof(uchar) * count);
|
||||
else
|
||||
__ERR__(__LOG_E__ << "Failed to allocate picture buffer (" << w << "x" << h << "@" << fmt.bpp << "bpp).\n", false);
|
||||
|
||||
width = w;
|
||||
height = h;
|
||||
pxformat.Set(fmt);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Picture::Clone(const Picture &src, bool clone_data)
|
||||
{
|
||||
Free();
|
||||
|
||||
if (src.IsStub())
|
||||
Stub(src.GetWidth(), src.GetHeight());
|
||||
else
|
||||
{
|
||||
if (clone_data)
|
||||
{
|
||||
if (AllocAs(src.GetWidth(), src.GetHeight(), src.pxformat.GetDesc()))
|
||||
Memory::Copy((char *)data, (char *)src.GetData(), width * height * (pxformat.GetBpp() / 8));
|
||||
}
|
||||
else
|
||||
SetData(src.GetData(), src.GetWidth(), src.GetHeight(), src.pxformat.GetDesc(), false);
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Picture::Stub(uint w, uint h)
|
||||
{
|
||||
Free();
|
||||
protected_flag.Set(PictureIsStub);
|
||||
width = w;
|
||||
height = h;
|
||||
}
|
||||
void Picture::Zeroify()
|
||||
{
|
||||
data = NULL;
|
||||
hash = 0;
|
||||
width = height = 0;
|
||||
pxformat.Set(PixelFormat::NONE);
|
||||
pic_flag = 0;
|
||||
protected_flag = 0;
|
||||
}
|
||||
void Picture::FreeData()
|
||||
{
|
||||
if (!pic_flag.IsSet(HasForeignData))
|
||||
FreeMemory(data);
|
||||
data = NULL;
|
||||
pic_flag.Remove(HasForeignData);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
uchar *Picture::AllocMemory(size_t size)
|
||||
{ return (uchar *)Alloc::DefaultAllocator::Alloc(size, Alloc::Picture); }
|
||||
void Picture::FreeMemory(uchar *data)
|
||||
{ Alloc::DefaultAllocator::Delete(data, Alloc::Picture); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Picture::~Picture() { Free(); }
|
||||
//------------------------------------------------------------------------------
|
||||
420
include/framework/picture/pict_blit.cpp
Normal file
420
include/framework/picture/pict_blit.cpp
Normal file
@ -0,0 +1,420 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
------------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
#include <cstring>
|
||||
#include "picture/pict.h"
|
||||
#include "color/color.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Picture::Reframe(int offset_sx, int offset_sy, int offset_ex, int offset_ey, const Color *fill)
|
||||
{
|
||||
if (!GetData() || (GetPixelFormat().GetBpp() != 32))
|
||||
return false;
|
||||
|
||||
int _width = GetWidth() - offset_sx + offset_ex,
|
||||
_height = GetHeight() - offset_sy + offset_ey;
|
||||
|
||||
uint *new_data = (uint *)AllocMemory(sizeof(uint) * _width * _height), *_d = new_data;
|
||||
|
||||
if (!new_data)
|
||||
__ERR__(__LOG_E__ << "Failed to allocate destination buffer.\n", false)
|
||||
|
||||
// Fill color.
|
||||
uint _fill = fill ? GetPixelFormat().Format(fill->x, fill->y, fill->z, fill->w) : GetPixelFormat().Format(0, 0, 0);
|
||||
|
||||
// Top framing.
|
||||
for (int y = offset_sy; y < 0; ++y)
|
||||
for (int x = 0; x < _width; ++x)
|
||||
*_d++ = _fill;
|
||||
|
||||
// Blit + left/right framing.
|
||||
int blit_ex = offset_ex > 0 ? GetWidth() : GetWidth() + offset_ex,
|
||||
blit_ey = offset_ey > 0 ? GetHeight() : GetHeight() + offset_ey;
|
||||
|
||||
uint *s = (uint *)GetData();
|
||||
if (offset_sy > 0)
|
||||
s += GetWidth() * offset_sy;
|
||||
|
||||
for (int y = offset_sy > 0 ? offset_sy : 0; y < blit_ey; ++y)
|
||||
{
|
||||
uint *_s = s;
|
||||
|
||||
for (int x = offset_sx; x < 0; ++x)
|
||||
*_d++ = _fill; // Left framing
|
||||
for (int x = offset_sx > 0 ? offset_sx : 0; x < blit_ex; ++x)
|
||||
*_d++ = _s[x]; // Blit
|
||||
for (int x = 0; x < offset_ex; ++x)
|
||||
*_d++ = _fill; // Right framing
|
||||
|
||||
s += GetWidth();
|
||||
}
|
||||
|
||||
// Bottom framing.
|
||||
for (int y = 0; y < offset_ey; ++y)
|
||||
for (int x = 0; x < _width; ++x)
|
||||
*_d++ = _fill;
|
||||
|
||||
SetData(new_data, _width, _height, GetPixelFormat().GetDesc(), true);
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Picture::Flip(bool flip_h, bool flip_v)
|
||||
{
|
||||
if (!GetData() || (GetPixelFormat().GetBpp() != 32))
|
||||
return false;
|
||||
|
||||
uint *s = (uint *)GetData(),
|
||||
*d = s, t;
|
||||
|
||||
if (flip_h)
|
||||
{
|
||||
if (flip_v)
|
||||
{
|
||||
d += GetWidth() * GetHeight() - 1;
|
||||
|
||||
while (d > s)
|
||||
{
|
||||
t = *s;
|
||||
*s++ = *d;
|
||||
*d-- = t;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (uint n = 0; n < GetHeight(); ++n)
|
||||
{
|
||||
uint *_s = s, *_d = d + GetWidth() - 1;
|
||||
|
||||
while (_d > _s)
|
||||
{
|
||||
t = *_s;
|
||||
*_s++ = *_d;
|
||||
*_d-- = t;
|
||||
}
|
||||
s += GetWidth();
|
||||
d += GetWidth();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
if (flip_v)
|
||||
{
|
||||
d += GetWidth() * (GetHeight() - 1);
|
||||
while (d > s)
|
||||
{
|
||||
for (uint n = 0; n < GetWidth(); ++n)
|
||||
{
|
||||
t = s[n];
|
||||
s[n] = d[n];
|
||||
d[n] = t;
|
||||
}
|
||||
s += GetWidth();
|
||||
d -= GetWidth();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static void WindowClip(const Rect <int> *a, Rect <int> *fa, const Rect <int> *b, Rect <int> *fb)
|
||||
{
|
||||
Rect <int> _b(*b);
|
||||
_b.ex = _b.sx + a->GetWidth();
|
||||
_b.ey = _b.sy + a->GetHeight();
|
||||
|
||||
// Clip source rectangle and correct destination rectangle.
|
||||
*fa = fa->Intersection(*a);
|
||||
_b.sx += fa->sx - a->sx;
|
||||
_b.sy += fa->sy - a->sy;
|
||||
_b.ex += fa->ex - a->ex;
|
||||
_b.ey += fa->ey - a->ey;
|
||||
|
||||
// Clip destination rectangle and correct source rectangle.
|
||||
*fb = fb->Intersection(_b);
|
||||
fa->sx += fb->sx - _b.sx;
|
||||
fa->sy += fb->sy - _b.sy;
|
||||
fa->ex += fb->ex - _b.ex;
|
||||
fa->ey += fb->ey - _b.ey;
|
||||
}
|
||||
static void WindowStretch(Rect <float> *a, Rect <float> *fa, Rect <float> *b, Rect <float> *fb)
|
||||
{
|
||||
float ku = (float)b->GetWidth() / (float)a->GetWidth(),
|
||||
kv = (float)b->GetHeight() / (float)a->GetHeight();
|
||||
|
||||
// Clip source rectangle and correct destination rectangle.
|
||||
fa[0] = fa->Intersection(a[0]);
|
||||
b->sx += (fa->sx - a->sx) * ku;
|
||||
b->sy += (fa->sy - a->sy) * kv;
|
||||
b->ex += (fa->ex - a->ex) * ku;
|
||||
b->ey += (fa->ey - a->ey) * kv;
|
||||
|
||||
// Clip destination rectangle and correct source rectangle.
|
||||
fb[0] = fb->Intersection(b[0]);
|
||||
ku = 1 / ku;
|
||||
kv = 1 / kv;
|
||||
fa->sx += (fb->sx - b->sx) * ku;
|
||||
fa->sy += (fb->sy - b->sy) * kv;
|
||||
fa->ex += (fb->ex - b->ex) * ku;
|
||||
fa->ey += (fb->ey - b->ey) * kv;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Picture::BlitMask(const Picture &src, Picture &dst, Picture &msk, const Rect <int> *src_rect, const Rect <int> *dst_rect)
|
||||
{
|
||||
if (!src.GetData() || !dst.GetData())
|
||||
return false;
|
||||
|
||||
// Default blit rectangles.
|
||||
Rect <int> default_a(src.GetRect()), default_b(dst.GetRect());
|
||||
if (!src_rect)
|
||||
src_rect = &default_a;
|
||||
if (!dst_rect)
|
||||
dst_rect = &default_b;
|
||||
|
||||
// Compute blit window.
|
||||
Rect <int> frame_a(src.GetRect()), frame_b(dst.GetRect());
|
||||
WindowClip(src_rect, &frame_a, dst_rect, &frame_b);
|
||||
|
||||
// Limit to mask dimensions.
|
||||
if (frame_b.GetWidth() > (int)msk.GetWidth())
|
||||
frame_b.SetWidth(msk.GetWidth());
|
||||
if (frame_b.GetHeight() > (int)msk.GetHeight())
|
||||
frame_b.SetHeight(msk.GetHeight());
|
||||
|
||||
if ((frame_b.GetWidth() <= 0) || (frame_b.GetHeight() <= 0))
|
||||
return false;
|
||||
|
||||
PixelFormatDescription initial = dst.GetPixelFormat().GetDesc();
|
||||
dst.Convert(src.GetPixelFormat().GetDesc());
|
||||
|
||||
// Perform blit.
|
||||
uchar *psrc = src.GetDataOffset(frame_a.sx, frame_a.sy),
|
||||
*pdst = dst.GetDataOffset(frame_b.sx, frame_b.sy),
|
||||
*pmsk = msk.GetData();
|
||||
|
||||
for (int n = 0; n < frame_b.GetHeight(); n++)
|
||||
{
|
||||
uchar *_pdst = pdst, *_psrc = psrc, *_pmsk = pmsk;
|
||||
|
||||
for (int x = frame_b.GetWidth(); x--; )
|
||||
{
|
||||
uchar alpha = _pmsk[3];
|
||||
|
||||
uchar a_blend = AlphaCompositeAlpha(_pdst[3], alpha);
|
||||
_pdst[0] = AlphaCompositeColor(_pdst[0], _psrc[0], _pdst[3], alpha, a_blend);
|
||||
_pdst[1] = AlphaCompositeColor(_pdst[1], _psrc[1], _pdst[3], alpha, a_blend);
|
||||
_pdst[2] = AlphaCompositeColor(_pdst[2], _psrc[2], _pdst[3], alpha, a_blend);
|
||||
_pdst[3] = a_blend;
|
||||
|
||||
_psrc += 4;
|
||||
_pdst += 4;
|
||||
_pmsk += 4;
|
||||
}
|
||||
|
||||
psrc += src.GetPitch();
|
||||
pdst += dst.GetPitch();
|
||||
pmsk += msk.GetPitch();
|
||||
}
|
||||
dst.Convert(initial);
|
||||
return true;
|
||||
}
|
||||
bool Picture::Blit(const Picture &src, Picture &dst, const Rect <int> *src_rect, const Rect <int> *dst_rect, BlendMode mode)
|
||||
{
|
||||
if (!src.GetData() || !dst.GetData())
|
||||
return false;
|
||||
|
||||
// Default blit rectangles.
|
||||
Rect <int> default_a(src.GetRect()), default_b(dst.GetRect());
|
||||
if (!src_rect)
|
||||
src_rect = &default_a;
|
||||
if (!dst_rect)
|
||||
dst_rect = &default_b;
|
||||
|
||||
// Compute blit window.
|
||||
Rect <int> frame_a(src.GetRect()), frame_b(dst.GetRect());
|
||||
WindowClip(src_rect, &frame_a, dst_rect, &frame_b);
|
||||
|
||||
if ((frame_b.GetWidth() <= 0) || (frame_b.GetHeight() <= 0))
|
||||
return false;
|
||||
|
||||
PixelFormatDescription initial = dst.GetPixelFormat().GetDesc();
|
||||
dst.Convert(src.GetPixelFormat().GetDesc());
|
||||
|
||||
// Perform blit.
|
||||
uchar *psrc = src.GetDataOffset(frame_a.sx, frame_a.sy),
|
||||
*pdst = dst.GetDataOffset(frame_b.sx, frame_b.sy);
|
||||
|
||||
for (int n = 0; n < frame_b.GetHeight(); n++)
|
||||
{
|
||||
uchar *_pdst = pdst, *_psrc = psrc;
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case RgbToAlpha:
|
||||
for (int x = frame_b.GetWidth(); x--; )
|
||||
{
|
||||
_pdst[3] = (_psrc[0] + _psrc[1] + _psrc[2]) / 3;
|
||||
_psrc += 4;
|
||||
_pdst += 4;
|
||||
}
|
||||
break;
|
||||
|
||||
case BlendReplace:
|
||||
memmove(pdst, psrc, frame_b.GetWidth() * dst.GetBpp() / 8);
|
||||
break;
|
||||
|
||||
case BlendComposeFast:
|
||||
for (int x = frame_b.GetWidth(); x--; )
|
||||
{
|
||||
int a = _psrc[3], ia = 255 - a;
|
||||
_pdst[0] = uchar((_psrc[0] * a + _pdst[0] * ia) >> 8);
|
||||
_pdst[1] = uchar((_psrc[1] * a + _pdst[1] * ia) >> 8);
|
||||
_pdst[2] = uchar((_psrc[2] * a + _pdst[2] * ia) >> 8);
|
||||
_pdst[3] = uchar(Types::Max <int> (_pdst[3], a));
|
||||
|
||||
_psrc += 4;
|
||||
_pdst += 4;
|
||||
}
|
||||
break;
|
||||
|
||||
case BlendCompose:
|
||||
for (int x = frame_b.GetWidth(); x--; )
|
||||
{
|
||||
uchar a_blend = AlphaCompositeAlpha(_pdst[3], _psrc[3]);
|
||||
_pdst[0] = AlphaCompositeColor(_pdst[0], _psrc[0], _pdst[3], _psrc[3], a_blend);
|
||||
_pdst[1] = AlphaCompositeColor(_pdst[1], _psrc[1], _pdst[3], _psrc[3], a_blend);
|
||||
_pdst[2] = AlphaCompositeColor(_pdst[2], _psrc[2], _pdst[3], _psrc[3], a_blend);
|
||||
_pdst[3] = a_blend;
|
||||
|
||||
_psrc += 4;
|
||||
_pdst += 4;
|
||||
}
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
psrc += src.GetPitch();
|
||||
pdst += dst.GetPitch();
|
||||
}
|
||||
dst.Convert(initial);
|
||||
return true;
|
||||
}
|
||||
bool Picture::ScaleBlit(Picture &src, Picture &dst, Rect <float> *src_rect, Rect <float> *dst_rect)
|
||||
{
|
||||
if (!src.GetData() || !dst.GetData())
|
||||
return false;
|
||||
|
||||
// Default blitting rectangles.
|
||||
Rect <float> default_a(src.GetRect().AsFloat()), default_b(dst.GetRect().AsFloat());
|
||||
if (!src_rect)
|
||||
src_rect = &default_a;
|
||||
if (!dst_rect)
|
||||
dst_rect = &default_b;
|
||||
|
||||
Rect <float> frame_a(src.GetRect().AsFloat()), frame_b(dst.GetRect().AsFloat());
|
||||
WindowStretch(src_rect, &frame_a, dst_rect, &frame_b);
|
||||
|
||||
if ((frame_b.GetWidth() <= 0) || (frame_b.GetHeight() <= 0))
|
||||
return false;
|
||||
|
||||
// Apply sub-pixel/sub-texel correction.
|
||||
float ku = frame_a.GetWidth() / frame_b.GetWidth(),
|
||||
kv = frame_a.GetHeight() / frame_b.GetHeight();
|
||||
|
||||
float fb_isx = Math::Floor(frame_b.sx),
|
||||
fb_isy = Math::Floor(frame_b.sy),
|
||||
fb_iex = Math::Floor(frame_b.ex),
|
||||
fb_iey = Math::Floor(frame_b.ey);
|
||||
|
||||
float dt_su = (frame_b.sx - fb_isx),
|
||||
dt_sv = (frame_b.sy - fb_isy),
|
||||
dt_eu = (frame_b.ex - fb_iex),
|
||||
dt_ev = (frame_b.ey - fb_iey);
|
||||
|
||||
frame_a.sx -= dt_su * ku;
|
||||
frame_a.sy -= dt_sv * kv;
|
||||
frame_a.ex -= dt_eu * ku;
|
||||
frame_a.ey -= dt_ev * kv;
|
||||
|
||||
frame_b.sx = fb_isx;
|
||||
frame_b.sy = fb_isy;
|
||||
frame_b.ex = fb_iex;
|
||||
frame_b.ey = fb_iey;
|
||||
|
||||
// Convert sub pixel deltas to blending coefficients.
|
||||
dt_su = 1 - dt_su;
|
||||
dt_sv = 1 - dt_sv;
|
||||
|
||||
uint *pdst = (uint *)dst.GetDataOffset((uint)frame_b.sx, (uint)frame_b.sy);
|
||||
|
||||
int src_width = (int)frame_b.GetWidth(),
|
||||
src_height = (int)frame_b.GetHeight();
|
||||
|
||||
float v = frame_a.sy;
|
||||
for (int y = 0; y < src_height; y++)
|
||||
{
|
||||
float u = frame_a.sx;
|
||||
|
||||
// Blended or opaque scanline.
|
||||
if ((!y) || (y == (src_height - 1)))
|
||||
{
|
||||
float kfrst, kscan, klast;
|
||||
|
||||
// Set blend coefficients.
|
||||
if (!y)
|
||||
{
|
||||
kfrst = dt_su * dt_sv; // top-left
|
||||
kscan = dt_sv; // top
|
||||
klast = dt_eu * dt_sv; // top-right
|
||||
}
|
||||
else
|
||||
{
|
||||
kfrst = dt_su * dt_ev; // bottom-left
|
||||
kscan = dt_ev; // bottom
|
||||
klast = dt_eu * dt_ev; // bottom-right
|
||||
}
|
||||
|
||||
// 1st pixel, scanline, last pixel.
|
||||
pdst[0] = ColorBlend(pdst[0], src.SampleInteger(u, v), kfrst);
|
||||
u += ku;
|
||||
|
||||
int x;
|
||||
for (x = 1; x < (src_width - 1); x++)
|
||||
{
|
||||
pdst[x] = ColorBlend(pdst[x], src.SampleInteger(u, v), kscan);
|
||||
u += ku;
|
||||
}
|
||||
pdst[x] = ColorBlend(pdst[x], src.SampleInteger(u, v), klast);
|
||||
}
|
||||
else
|
||||
{
|
||||
pdst[0] = ColorBlend(pdst[0], src.SampleInteger(u, v), dt_su);
|
||||
u += ku;
|
||||
|
||||
int x;
|
||||
for (x = 1; x < (src_width - 1); x++)
|
||||
{
|
||||
pdst[x] = src.SampleInteger(u, v);
|
||||
u += ku;
|
||||
}
|
||||
pdst[x] = ColorBlend(pdst[x], src.SampleInteger(u, v), dt_eu);
|
||||
}
|
||||
|
||||
pdst += dst.GetWidth();
|
||||
v += kv;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
137
include/framework/picture/pict_bmp.cpp
Normal file
137
include/framework/picture/pict_bmp.cpp
Normal file
@ -0,0 +1,137 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
------------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
#include "picture/pict.h"
|
||||
#include "picture/pict_io.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
|
||||
#define IMGBMP_BI_RGB 0
|
||||
#define IMGBMP_BI_RLE8 1
|
||||
#define IMGBMP_BI_RLE4 2
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool PictureIO::BmpLoad(Picture &picture, IO::Handle &handle)
|
||||
{
|
||||
handle.Rewind();
|
||||
size_t size = handle.GetSize();
|
||||
if (size < 10)
|
||||
return false;
|
||||
|
||||
// Magic number
|
||||
if ((handle.Read <char> () != 'B') || (handle.Read <char> () != 'M'))
|
||||
return false;
|
||||
|
||||
handle.Seek(8);
|
||||
uint OffBits = handle.Read <uint> (), width, height;
|
||||
ushort bpp;
|
||||
|
||||
if (handle.Read <uint> () == 40) // we met a BITMAPCOREHEADER
|
||||
{
|
||||
width = handle.Read <uint> ();
|
||||
height = handle.Read <uint> ();
|
||||
}
|
||||
else // we met a BITMAPINFOHEADER
|
||||
{
|
||||
width = handle.Read <ushort> ();
|
||||
height = handle.Read <ushort> ();
|
||||
}
|
||||
handle.Seek(2);
|
||||
bpp = handle.Read <ushort> ();
|
||||
|
||||
if ((bpp != 8) && (bpp != 24) && (bpp != 32))
|
||||
__ERR__(__LOG_E__ << "BMP format unhandled (neither RGB8, RGB24 or BGR8).\n", false)
|
||||
|
||||
// Decode bitmap data.
|
||||
if (!picture.AllocAs(width, height, PixelFormat::BGR8))
|
||||
__ERR__(__LOG_E__ << "Failed to allocate output buffer.\n", false)
|
||||
|
||||
uint *rgb = (uint *)picture.GetData();
|
||||
Array <uchar> bmp(size - OffBits);
|
||||
if (!bmp)
|
||||
__ERR__(__LOG_E__ << "Failed to allocate input framebuffer.\n", false)
|
||||
|
||||
uchar *_bmp = &bmp[0];
|
||||
handle.Seek(OffBits, GS::IO::Base::SeekStart);
|
||||
handle.Read((void *)_bmp, OffBits);
|
||||
|
||||
rgb += (picture.GetHeight() - 1) * picture.GetWidth();
|
||||
|
||||
// Load palette
|
||||
Array <uint> palette;
|
||||
|
||||
if (bpp < 16)
|
||||
{
|
||||
__LOG__ << "Picture is palletized.\n";
|
||||
if (palette.Allocate(1 << bpp))
|
||||
{
|
||||
char cbuf[4];
|
||||
handle.Seek(54, GS::IO::Base::SeekStart);
|
||||
for (int n = 0; n < (1 << bpp); ++n)
|
||||
{
|
||||
handle.Read(cbuf, 4);
|
||||
palette[n] = (cbuf[3] << 24) + (cbuf[2] << 16) + (cbuf[1] << 8) + cbuf[0];
|
||||
}
|
||||
}
|
||||
else
|
||||
__ERR__(__LOG_E__ << "Failed to allocate palette.\n", false)
|
||||
}
|
||||
|
||||
// Even width
|
||||
switch (bpp)
|
||||
{
|
||||
case 8:
|
||||
{
|
||||
// 32 bit 0 padding.
|
||||
uint pad = 0;
|
||||
if (picture.GetWidth() & 3)
|
||||
pad = 4 - (picture.GetWidth() & 3);
|
||||
|
||||
for (uint c2 = 0; c2 < picture.GetHeight(); c2++)
|
||||
{
|
||||
for (uint c = 0; c < picture.GetWidth(); c++)
|
||||
rgb[c] = palette[*_bmp++];
|
||||
_bmp += pad;
|
||||
rgb -= picture.GetWidth();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 16:
|
||||
break;
|
||||
|
||||
case 24:
|
||||
for (uint c2 = 0; c2 < picture.GetHeight(); c2++)
|
||||
{
|
||||
for (uint c = 0; c < picture.GetWidth(); c++)
|
||||
{
|
||||
rgb[c] = (_bmp[2] << 16) + (_bmp[1] << 8) + _bmp[0];
|
||||
_bmp += 3;
|
||||
}
|
||||
uint dt = 3 * picture.GetWidth();
|
||||
if (dt & 3)
|
||||
_bmp += (4 - (dt & 3));
|
||||
rgb -= picture.GetWidth();
|
||||
}
|
||||
break;
|
||||
|
||||
case 32:
|
||||
for (uint c2 = 0; c2 < picture.GetHeight(); c2++)
|
||||
{
|
||||
for (uint c = 0; c < picture.GetWidth(); c++)
|
||||
{
|
||||
rgb[c] = (_bmp[2] << 16) + (_bmp[1] << 8) + _bmp[0];
|
||||
_bmp += 4;
|
||||
}
|
||||
rgb -= picture.GetWidth();
|
||||
}
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
333
include/framework/picture/pict_color_format.cpp
Normal file
333
include/framework/picture/pict_color_format.cpp
Normal file
@ -0,0 +1,333 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "picture/pict_color_format.h"
|
||||
#include "picture/pict.h"
|
||||
#include "math/nmath.h"
|
||||
#include "sort/sort.h"
|
||||
#include "memory/endian.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
|
||||
|
||||
PixelFormatDescription PixelFormat::NONE
|
||||
= {PixelColorSpace_NULL, 0, 0, 0, 0, 0, false};
|
||||
PixelFormatDescription PixelFormat::BGRA8
|
||||
= {PixelColorSpace_RGB, 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff, 32, false};
|
||||
PixelFormatDescription PixelFormat::RGBA8
|
||||
= {PixelColorSpace_RGB, 0xff000000, 0x000000ff, 0x0000ff00, 0x00ff0000, 32, false};
|
||||
PixelFormatDescription PixelFormat::ARGB8
|
||||
= {PixelColorSpace_RGB, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000, 32, false};
|
||||
PixelFormatDescription PixelFormat::BGR8
|
||||
= {PixelColorSpace_RGB, 0, 0x00ff0000, 0x0000ff00, 0x000000ff, 24, false};
|
||||
PixelFormatDescription PixelFormat::RGB8
|
||||
= {PixelColorSpace_RGB, 0, 0x000000ff, 0x0000ff00,0x00ff0000, 24, false};
|
||||
PixelFormatDescription PixelFormat::RGB555
|
||||
= {PixelColorSpace_RGB, 0, 0x0000001f, 0x000003e0, 0x00007c00, 16, false};
|
||||
PixelFormatDescription PixelFormat::RGB565
|
||||
= {PixelColorSpace_RGB, 0, 0x0000001f, 0x000007e0, 0x0000f800, 16, false};
|
||||
PixelFormatDescription PixelFormat::RGBA4444
|
||||
= {PixelColorSpace_RGB, 0x0000f000, 0x0000000f, 0x000000f0, 0x00000f00, 16, false};
|
||||
PixelFormatDescription PixelFormat::RGBF
|
||||
= { PixelColorSpace_RGB, 0, 0, 1, 2, sizeof(float) * 3 * 8, true };
|
||||
PixelFormatDescription PixelFormat::RGBAF
|
||||
= { PixelColorSpace_RGB, 0xff000000, 0x000000ff, 0x0000ff00, 0x00ff0000, 4 * 16, true };
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
String PixelFormat::GetName() const
|
||||
{
|
||||
// Sort components.
|
||||
uint count[4] = { Memory::GetBitCount(desc.rmask), Memory::GetBitCount(desc.gmask), Memory::GetBitCount(desc.bmask), Memory::GetBitCount(desc.amask) },
|
||||
shift[4] = { Memory::GetShiftCount(desc.rmask), Memory::GetShiftCount(desc.gmask), Memory::GetShiftCount(desc.bmask), Memory::GetShiftCount(desc.amask) };
|
||||
|
||||
Sort<uint, uint>::Entry comp_sort[4];
|
||||
for (uint n = 0; n < 4; ++n)
|
||||
{
|
||||
comp_sort[n].v = shift[n];
|
||||
comp_sort[n].o = n;
|
||||
}
|
||||
Sort<uint, uint>::QuickSort(4, comp_sort);
|
||||
|
||||
// Build name.
|
||||
static const char *comp_name[4] = {"R", "G", "B", "A"};
|
||||
|
||||
String name;
|
||||
switch (desc.space)
|
||||
{
|
||||
case PixelColorSpace_RGB:
|
||||
{
|
||||
for (uint n = 0; n < 4; ++n)
|
||||
{
|
||||
int i = comp_sort[n].o;
|
||||
if (count[i] != 0)
|
||||
{
|
||||
name += String::Format("%s%d", comp_name[i], count[i]);
|
||||
if (desc.real)
|
||||
name += "F";
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
name = "NONE";
|
||||
break;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
uint PixelFormat::Format(float r, float g, float b, float a) const
|
||||
{
|
||||
switch (GetBpp())
|
||||
{
|
||||
case 32:
|
||||
return Endian::ToHost((int(r * 255) << rshift) + (int(g * 255) << gshift) + (int(b * 255) << bshift) + (int(a * 255) << ashift), Endian::Intel);
|
||||
|
||||
default:
|
||||
__LOG_W__ << "Unimplemented formatting.\n";
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Picture::Negative(bool r, bool g, bool b, bool a)
|
||||
{
|
||||
if ((GetBpp() != 32) || (!r && !g && !b && !a) || !GetData())
|
||||
return;
|
||||
|
||||
int iia = pxformat.ashift >> 3,
|
||||
iir = pxformat.rshift >> 3,
|
||||
iig = pxformat.gshift >> 3,
|
||||
iib = pxformat.bshift >> 3;
|
||||
|
||||
unsigned char *p = GetData();
|
||||
for (uint v = 0; v < height; ++v)
|
||||
for (uint u = 0; u < width; ++u)
|
||||
{
|
||||
if (r) p[iir] = 255 - p[iir];
|
||||
if (g) p[iig] = 255 - p[iig];
|
||||
if (b) p[iib] = 255 - p[iib];
|
||||
if (a) p[iia] = 255 - p[iia];
|
||||
p += 4;
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Picture::Fast8888Conversion(const PixelFormat &dsformat)
|
||||
{
|
||||
int iia = pxformat.ashift >> 3,
|
||||
iir = pxformat.rshift >> 3,
|
||||
iig = pxformat.gshift >> 3,
|
||||
iib = pxformat.bshift >> 3,
|
||||
dia = dsformat.ashift >> 3,
|
||||
dir = dsformat.rshift >> 3,
|
||||
dig = dsformat.gshift >> 3,
|
||||
dib = dsformat.bshift >> 3;
|
||||
|
||||
unsigned char *p = GetData();
|
||||
if (!p)
|
||||
return false;
|
||||
|
||||
// Optimized for the most common reorganizing done on 8888 ARGB data.
|
||||
unsigned char r, g, b, a;
|
||||
if (iia == dia)
|
||||
{
|
||||
if (iig == dig) // Fixed green & alpha.
|
||||
for (uint v = 0; v < GetHeight(); ++v)
|
||||
for (uint u = 0; u < GetWidth(); ++u)
|
||||
{
|
||||
r = p[iir]; b = p[iib];
|
||||
p[dir] = r; p[dib] = b;
|
||||
p += 4;
|
||||
}
|
||||
else // Fixed alpha.
|
||||
for (uint v = 0; v < GetHeight(); ++v)
|
||||
for (uint u = 0; u < GetWidth(); ++u)
|
||||
{
|
||||
r = p[iir]; g = p[iig]; b = p[iib];
|
||||
p[dir] = r; p[dig] = g; p[dib] = b;
|
||||
p += 4;
|
||||
}
|
||||
}
|
||||
else // Fully generic.
|
||||
for (uint v = 0; v < GetHeight(); ++v)
|
||||
for (uint u = 0; u < GetWidth(); ++u)
|
||||
{
|
||||
a = p[iia]; r = p[iir]; g = p[iig]; b = p[iib];
|
||||
p[dia] = a; p[dir] = r; p[dig] = g; p[dib] = b;
|
||||
p += 4;
|
||||
}
|
||||
|
||||
pxformat = dsformat;
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Picture::RealToIntegerConversion(const PixelFormat &dstfmt)
|
||||
{
|
||||
uchar *new_data = new uchar[width * height * (dstfmt.GetBpp() / 8)];
|
||||
if (!new_data)
|
||||
return false;
|
||||
|
||||
uchar *out = new_data;
|
||||
float *pdata = (float *)GetData();
|
||||
|
||||
// Convert.
|
||||
for (uint y = 0; y < GetHeight(); ++y)
|
||||
for (uint x = 0; x < GetWidth(); ++x)
|
||||
{
|
||||
uchar r = (uchar)Types::Min(pdata[2] * 255.f, 255.f), g = (uchar)Types::Min(pdata[1] * 255.f, 255.f), b = (uchar)Types::Min(pdata[0] * 255.f, 255.f), a = 255;
|
||||
pdata += 3;
|
||||
|
||||
// Convert components.
|
||||
a >>= (8 - dstfmt.acount);
|
||||
r >>= (8 - dstfmt.rcount);
|
||||
g >>= (8 - dstfmt.gcount);
|
||||
b >>= (8 - dstfmt.bcount);
|
||||
|
||||
// Repack components and output.
|
||||
uint packed;
|
||||
uchar *ppack = (uchar *)&packed;
|
||||
|
||||
packed = (a << dstfmt.ashift) + (r << dstfmt.rshift) + (g << dstfmt.gshift) + (b << dstfmt.bshift);
|
||||
|
||||
switch (dstfmt.GetBpp())
|
||||
{
|
||||
case 8: out[0] = ppack[0]; out++; break;
|
||||
case 16: out[0] = ppack[0]; out[1] = ppack[1]; out += 2; break;
|
||||
case 24: out[0] = ppack[0]; out[1] = ppack[1]; out[2] = ppack[2]; out += 3; break;
|
||||
case 32: out[0] = ppack[0]; out[1] = ppack[1]; out[2] = ppack[2]; out[3] = ppack[3]; out += 4; break;
|
||||
}
|
||||
}
|
||||
|
||||
// Replace data.
|
||||
SetData(new_data, width, height, dstfmt.GetDesc());
|
||||
return true;
|
||||
}
|
||||
bool Picture::IntegerToIntegerConversion(const PixelFormat &dsformat)
|
||||
{
|
||||
switch (pxformat.GetBpp())
|
||||
{
|
||||
case 8: case 16: case 24: case 32: break;
|
||||
default: return false; // Unsupported source mode.
|
||||
}
|
||||
|
||||
// Allocate destination buffer.
|
||||
uchar *new_data = new uchar[width * height * (dsformat.GetBpp() / 8)];
|
||||
if (!new_data)
|
||||
return false;
|
||||
|
||||
uchar *out = new_data;
|
||||
uchar *pdata = GetData();
|
||||
|
||||
// Convert.
|
||||
for (uint y = 0; y < GetHeight(); ++y)
|
||||
{
|
||||
uint packed;
|
||||
uchar *ppack = (uchar *)&packed;
|
||||
|
||||
for (uint x = 0; x < GetWidth(); ++x)
|
||||
{
|
||||
// Extract packed color.
|
||||
switch (pxformat.GetBpp())
|
||||
{
|
||||
case 8: ppack[0] = pdata[0]; pdata++; break;
|
||||
case 16: ppack[0] = pdata[0]; ppack[1] = pdata[1]; pdata += 2; break;
|
||||
case 24: ppack[0] = pdata[0]; ppack[1] = pdata[1]; ppack[2] = pdata[2]; pdata += 3; break;
|
||||
case 32: ppack[0] = pdata[0]; ppack[1] = pdata[1]; ppack[2] = pdata[2]; ppack[3] = pdata[3]; pdata += 4; break;
|
||||
}
|
||||
|
||||
// Extract components.
|
||||
uint a = (uchar)(((packed & pxformat.desc.amask) >> pxformat.ashift) << (8 - pxformat.acount)),
|
||||
r = (uchar)(((packed & pxformat.desc.rmask) >> pxformat.rshift) << (8 - pxformat.rcount)),
|
||||
g = (uchar)(((packed & pxformat.desc.gmask) >> pxformat.gshift) << (8 - pxformat.gcount)),
|
||||
b = (uchar)(((packed & pxformat.desc.bmask) >> pxformat.bshift) << (8 - pxformat.bcount));
|
||||
|
||||
// Convert components.
|
||||
a >>= (8 - dsformat.acount);
|
||||
r >>= (8 - dsformat.rcount);
|
||||
g >>= (8 - dsformat.gcount);
|
||||
b >>= (8 - dsformat.bcount);
|
||||
|
||||
// Repack components and output.
|
||||
packed = Endian::ToHost((a << dsformat.ashift) + (r << dsformat.rshift) + (g << dsformat.gshift) + (b << dsformat.bshift), Endian::Intel);
|
||||
|
||||
switch (dsformat.GetBpp())
|
||||
{
|
||||
case 8: out[0] = ppack[0]; out++; break;
|
||||
case 16: out[0] = ppack[0]; out[1] = ppack[1]; out += 2; break;
|
||||
case 24: out[0] = ppack[0]; out[1] = ppack[1]; out[2] = ppack[2]; out += 3; break;
|
||||
case 32: out[0] = ppack[0]; out[1] = ppack[1]; out[2] = ppack[2]; out[3] = ppack[3]; out += 4; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Replace data.
|
||||
SetData(new_data, width, height, dsformat.GetDesc(), true);
|
||||
return true;
|
||||
}
|
||||
bool Picture::Convert(const PixelFormatDescription &dsc)
|
||||
{
|
||||
if (pxformat == dsc)
|
||||
return true;
|
||||
|
||||
if (!dsc.bpp)
|
||||
{
|
||||
Free();
|
||||
return true;
|
||||
}
|
||||
|
||||
PixelFormat dsformat(dsc);
|
||||
|
||||
// Real to integer.
|
||||
if (pxformat.IsReal())
|
||||
return RealToIntegerConversion(dsformat);
|
||||
|
||||
// Fast 8888 conversion.
|
||||
if (
|
||||
(pxformat.acount == 8) && (pxformat.rcount == 8) && (pxformat.gcount == 8) && (pxformat.bcount == 8) &&
|
||||
(dsformat.acount == 8) && (dsformat.rcount == 8) && (dsformat.gcount == 8) && (dsformat.bcount == 8)
|
||||
)
|
||||
return Fast8888Conversion(dsformat);
|
||||
|
||||
// Slower generic integer->integer conversion.
|
||||
return IntegerToIntegerConversion(dsformat);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Picture::Swizzle(uchar r, uchar g, uchar b, uchar a)
|
||||
{
|
||||
// Swizzle output format...
|
||||
PixelFormatDescription out_format = GetPixelFormat().GetDesc();
|
||||
|
||||
const uint in_mask[4] = { out_format.rmask, out_format.gmask, out_format.bmask, out_format.amask };
|
||||
out_format.rmask = in_mask[r];
|
||||
out_format.gmask = in_mask[g];
|
||||
out_format.bmask = in_mask[b];
|
||||
out_format.amask = in_mask[a];
|
||||
|
||||
// ...and convert picture.
|
||||
return Convert(out_format);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Picture::SetFormat(const PixelFormatDescription &dsc)
|
||||
{
|
||||
if (dsc.bpp != pxformat.GetDesc().bpp)
|
||||
__ERR__(__LOG_E__ << "Target format requires a data conversion, see Convert().\n", false)
|
||||
|
||||
pxformat.Set(dsc);
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
94
include/framework/picture/pict_convolution.cpp
Normal file
94
include/framework/picture/pict_convolution.cpp
Normal file
@ -0,0 +1,94 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "picture/pict.h"
|
||||
#include "color/color.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Picture::ApplyConvolution(uint k_width, uint k_height, const int *p_w, int weight, int pass, const Rect <int> *clip_rect)
|
||||
{
|
||||
if (GetBpp() != 32)
|
||||
__ERR__(__LOG_E__ << "Convolution filter only supported on 32bpp picture.\n", false);
|
||||
if (!k_width || !k_height)
|
||||
__ERR__(__LOG_E__ << "Invalid kernel size (" << k_width << "x" << k_height << ").\n", false);
|
||||
|
||||
if (pass <= 0)
|
||||
return true;
|
||||
|
||||
// Clipping rect.
|
||||
Rect <int> rect = GetRect();
|
||||
if (!clip_rect)
|
||||
clip_rect = ▭
|
||||
|
||||
// Setup flip chain.
|
||||
Picture tmp(*this), *src, *dst;
|
||||
|
||||
if (pass & 1)
|
||||
{ src = &tmp; dst = this; }
|
||||
else { src = this; dst = &tmp; }
|
||||
|
||||
// Perform convolution.
|
||||
for (int p = 0; p < pass; ++p)
|
||||
{
|
||||
uchar *pdata = dst->GetDataOffset(clip_rect->sx, clip_rect->sy);
|
||||
|
||||
for (int y = 0; y < clip_rect->GetHeight(); ++y)
|
||||
{
|
||||
uchar *pscan = pdata;
|
||||
|
||||
for (int x = 0; x < rect.GetWidth(); ++x)
|
||||
{
|
||||
int sx = x - k_width / 2,
|
||||
sy = y - k_height / 2;
|
||||
|
||||
int k_sx = Types::Max <int> (sx, clip_rect->sx),
|
||||
k_sy = Types::Max <int> (sy, clip_rect->sy);
|
||||
int k_ex = Types::Min <int> (k_width + x - k_width / 2, clip_rect->ex),
|
||||
k_ey = Types::Min <int> (k_height + y - k_height / 2, clip_rect->ey);
|
||||
|
||||
const int *w = p_w + Types::Max(0, clip_rect->sx - sx)
|
||||
+ Types::Max(0, clip_rect->sy - sy) * k_width;
|
||||
|
||||
int k = 0;
|
||||
int accu[4] = { 0, 0, 0, 0 };
|
||||
for (int ky = k_sy; ky < k_ey; ++ky)
|
||||
{
|
||||
const int *sw = w;
|
||||
|
||||
for (int kx = k_sx; kx < k_ex; ++kx)
|
||||
{
|
||||
uchar *psrc = src->GetDataOffset(kx, ky);
|
||||
|
||||
accu[0] += psrc[0] * *sw;
|
||||
accu[1] += psrc[1] * *sw;
|
||||
accu[2] += psrc[2] * *sw;
|
||||
accu[3] += psrc[3] * *sw;
|
||||
|
||||
k += *sw++;
|
||||
}
|
||||
w += k_width;
|
||||
}
|
||||
|
||||
k = k ? (weight << 6) / k : (weight << 6);
|
||||
pscan[0] = (uchar)Types::Clamp((accu[0] * k) >> 14, 0, 255);
|
||||
pscan[1] = (uchar)Types::Clamp((accu[1] * k) >> 14, 0, 255);
|
||||
pscan[2] = (uchar)Types::Clamp((accu[2] * k) >> 14, 0, 255);
|
||||
pscan[3] = (uchar)Types::Clamp((accu[3] * k) >> 14, 0, 255);
|
||||
|
||||
pscan += 4;
|
||||
}
|
||||
pdata += dst->GetPitch();
|
||||
}
|
||||
|
||||
Picture *swp = dst; dst = src; src = swp;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
25
include/framework/picture/pict_draw.cpp
Normal file
25
include/framework/picture/pict_draw.cpp
Normal file
@ -0,0 +1,25 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "picture/pict.h"
|
||||
|
||||
using namespace GS;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
uchar Picture::AlphaCompositeAlpha(uchar a, uchar b)
|
||||
{ return (uchar)Types::Clamp <int> (a + b - ((a * b) >> 8), 0, 255); }
|
||||
uchar Picture::AlphaCompositeColor(uchar u, uchar v, uchar a, uchar b, uchar k)
|
||||
{ return (uchar)Types::Clamp <int> (k ? (u * a + v * b - ((u * b * a) >> 8)) / k : 0, 0, 255); }
|
||||
void Picture::AlphaCompositePixel(uchar *data, uchar r, uchar g, uchar b, uchar a)
|
||||
{
|
||||
uchar a_blend = AlphaCompositeAlpha(data[3], a);
|
||||
data[0] = AlphaCompositeColor(data[0], r, data[3], a, a_blend);
|
||||
data[1] = AlphaCompositeColor(data[1], g, data[3], a, a_blend);
|
||||
data[2] = AlphaCompositeColor(data[2], b, data[3], a, a_blend);
|
||||
data[3] = a_blend;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
166
include/framework/picture/pict_draw_line.cpp
Normal file
166
include/framework/picture/pict_draw_line.cpp
Normal file
@ -0,0 +1,166 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "picture/pict.h"
|
||||
#include "math/nmath.h"
|
||||
|
||||
using namespace GS;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Picture::LowLevelDrawLine(bool hq, float sx, float sy, float ex, float ey, float r, float g, float b, float a, const Rect <float> *clip_rect)
|
||||
{
|
||||
// Output validity.
|
||||
if (!data || (pxformat.GetBpp() != 32) || pxformat.IsReal())
|
||||
return;
|
||||
|
||||
// Clip primitive.
|
||||
float dx = ex - sx, dy = ey - sy;
|
||||
|
||||
// Sub pixel correction.
|
||||
if (Types::Abs(dy) > Types::Abs(dx))
|
||||
{
|
||||
float fsy = Math::Floor(sy);
|
||||
if (dy)
|
||||
sx -= dx * (sy - fsy) / dy; // Sub-pixel correction.
|
||||
sy = fsy;
|
||||
}
|
||||
else
|
||||
{
|
||||
float fsx = Math::Floor(sx);
|
||||
if (dx)
|
||||
sy -= dy * (sx - fsx) / dx; // Sub-pixel correction.
|
||||
sx = fsx;
|
||||
}
|
||||
|
||||
// Clipping.
|
||||
if (clip_rect)
|
||||
{
|
||||
//---------------------------------------------------------------
|
||||
#define __SwapFloat(A, B) { float swp = A; A = B; B = swp; }
|
||||
//---------------------------------------------------------------
|
||||
|
||||
if (dx)
|
||||
{
|
||||
// Flip line if it is going backward.
|
||||
if (dx < 0)
|
||||
{
|
||||
__SwapFloat(sx, ex)
|
||||
__SwapFloat(sy, ey)
|
||||
dx = ex - sx; dy = ey - sy;
|
||||
}
|
||||
float idx = 1 / dx;
|
||||
|
||||
// Clip on X axis.
|
||||
if (ex < clip_rect->sx)
|
||||
return;
|
||||
|
||||
float kee = (clip_rect->ex - sx) * idx;
|
||||
if (kee < 1)
|
||||
{
|
||||
ex = clip_rect->ex;
|
||||
ey = dy * kee + sy;
|
||||
}
|
||||
|
||||
if (sx > clip_rect->ex)
|
||||
return;
|
||||
|
||||
float kss = (clip_rect->sx - sx) * idx;
|
||||
if (kss > 0)
|
||||
{
|
||||
sx = clip_rect->sx;
|
||||
sy += dy * kss;
|
||||
}
|
||||
dx = ex - sx; dy = ey - sy;
|
||||
}
|
||||
else
|
||||
if ((sx < clip_rect->sx) || (sx > clip_rect->ex))
|
||||
return;
|
||||
|
||||
if (dy)
|
||||
{
|
||||
// Flip line if it is going backward.
|
||||
if (dy < 0)
|
||||
{
|
||||
__SwapFloat(sx, ex)
|
||||
__SwapFloat(sy, ey)
|
||||
dx = ex - sx; dy = ey - sy;
|
||||
}
|
||||
float idy = 1 / dy;
|
||||
|
||||
// Clip on Y axis.
|
||||
if (ey < clip_rect->sy)
|
||||
return;
|
||||
|
||||
float kee = (clip_rect->ey - sy) * idy;
|
||||
if (kee < 1)
|
||||
{
|
||||
ey = clip_rect->ey;
|
||||
ex = dx * kee + sx;
|
||||
}
|
||||
|
||||
if (sy > clip_rect->ey)
|
||||
return;
|
||||
|
||||
float kss = (clip_rect->sy - sy) * idy;
|
||||
if (kss > 0)
|
||||
{
|
||||
sy = clip_rect->sy;
|
||||
sx += dx * kss;
|
||||
}
|
||||
dx = ex - sx; dy = ey - sy;
|
||||
}
|
||||
else
|
||||
if ((sy < clip_rect->sy) || (sy > clip_rect->ey))
|
||||
return;
|
||||
}
|
||||
|
||||
// Draw line.
|
||||
if (Types::Abs(dy) > Types::Abs(dx))
|
||||
{
|
||||
if (dy < 0)
|
||||
{
|
||||
dy = -dy; dx = -dx;
|
||||
float swp = ey;
|
||||
ey = sy; sy = swp; sx = ex;
|
||||
}
|
||||
|
||||
float slope = dy ? dx / dy : 0.f;
|
||||
for (; sy < ey; sy += 1.f)
|
||||
{
|
||||
if (hq)
|
||||
DrawPlotHQ(sx, sy, r, g, b, a);
|
||||
else DrawPlot(sx, sy, r, g, b, a);
|
||||
sx += slope;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dx < 0)
|
||||
{
|
||||
dx = -dx; dy = -dy;
|
||||
float swp = ex;
|
||||
ex = sx; sx = swp; sy = ey;
|
||||
}
|
||||
|
||||
float slope = dx ? dy / dx : 0.f;
|
||||
for (; sx < ex; sx += 1.f)
|
||||
{
|
||||
if (hq)
|
||||
DrawPlotHQ(sx, sy, r, g, b, a);
|
||||
else DrawPlot(sx, sy, r, g, b, a);
|
||||
sy += slope;
|
||||
}
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Picture::DrawLine(float sx, float sy, float ex, float ey, float r, float g, float b, float a, const Rect <float> *clip_rect)
|
||||
{ LowLevelDrawLine(false, sx, sy, ex, ey, r, g, b, a, clip_rect); }
|
||||
void Picture::DrawLineHQ(float sx, float sy, float ex, float ey, float r, float g, float b, float a, const Rect <float> *clip_rect)
|
||||
{ LowLevelDrawLine(true, sx, sy, ex, ey, r, g, b, a, clip_rect); }
|
||||
//------------------------------------------------------------------------------
|
||||
60
include/framework/picture/pict_draw_plot.cpp
Normal file
60
include/framework/picture/pict_draw_plot.cpp
Normal file
@ -0,0 +1,60 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "picture/pict.h"
|
||||
#include "math/nmath.h"
|
||||
|
||||
using namespace GS;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Picture::DrawPlot(float x, float y, float r, float g, float b, float a, const Rect <float> *clip_rect)
|
||||
{
|
||||
// Output validity.
|
||||
if (!data || (pxformat.GetBpp() != 32) || pxformat.IsReal())
|
||||
return;
|
||||
|
||||
// Clip primitive.
|
||||
if (clip_rect && ((x < clip_rect->sx) || (x >= clip_rect->ex) || (y < clip_rect->sy) || (y >= clip_rect->ey)))
|
||||
return;
|
||||
|
||||
// Draw.
|
||||
AlphaCompositePixel(GetDataOffset((uint)x, (uint)y), uchar(r * 255.f), uchar(g * 255.f), uchar(b * 255.f), uchar(a * 255.f));
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Picture::DrawPlotHQ(float x, float y, float r, float g, float b, float a, const Rect <float> *clip_rect)
|
||||
{
|
||||
// Output validity.
|
||||
if (!data || (pxformat.GetBpp() != 32) || pxformat.IsReal())
|
||||
return;
|
||||
|
||||
// Clip primitive.
|
||||
if (clip_rect && ((x < clip_rect->sx) || (x >= (clip_rect->ex - 1)) || (y < clip_rect->sy) || (y >= (clip_rect->ey - 1))))
|
||||
return;
|
||||
|
||||
// Draw.
|
||||
uchar ir = uchar(r * 255.f),
|
||||
ig = uchar(g * 255.f),
|
||||
ib = uchar(b * 255.f),
|
||||
ia = uchar(a * 255.f);
|
||||
|
||||
float xm = Math::Floor(x),
|
||||
ym = Math::Floor(y);
|
||||
|
||||
float a0 = (xm + 1 - x) * (ym + 1 - y),
|
||||
a1 = (x - xm) * (ym + 1 - y),
|
||||
a2 = (xm + 1 - x) * (y - ym),
|
||||
a3 = (x - xm) * (y - ym);
|
||||
|
||||
uchar *output = GetDataOffset((uint)x, (uint)y);
|
||||
AlphaCompositePixel(output, ir, ig, ib, uchar(ia * a0));
|
||||
AlphaCompositePixel(output + 4, ir, ig, ib, uchar(ia * a1));
|
||||
AlphaCompositePixel(output + width * 4, ir, ig, ib, uchar(ia * a2));
|
||||
AlphaCompositePixel(output + (width + 1) * 4, ir, ig, ib, uchar(ia * a3));
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
159
include/framework/picture/pict_draw_polygon.cpp
Normal file
159
include/framework/picture/pict_draw_polygon.cpp
Normal file
@ -0,0 +1,159 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "picture/pict.h"
|
||||
#include "math/nmath.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
static bool ClipValueTestLess(float v, float c)
|
||||
{ return v < c; }
|
||||
static bool ClipValueTestGreater(float v, float c)
|
||||
{ return v >= c; }
|
||||
//-------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static uint ClipPolygonAxis(float clip, uint axis, bool (*ClipValueTest)(float v, float c), uint count_in, Point <float> *p_in, Point <float> *p_out)
|
||||
{
|
||||
uint p_current = 0, count_out = 0;
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------
|
||||
#define __ClipSegment\
|
||||
{\
|
||||
float k = (clip - p_in[p_current][axis]) / (p_in[p_next][axis] - p_in[p_current][axis]);\
|
||||
if (!axis)\
|
||||
p_out[count_out++].Set (clip, (p_in[p_next].y - p_in[p_current].y) * k + p_in[p_current].y);\
|
||||
else p_out[count_out++].Set ((p_in[p_next].x - p_in[p_current].x) * k + p_in[p_current].x, clip);\
|
||||
}
|
||||
//--------------------------------------------------------------------------------------------------------
|
||||
|
||||
while (p_current < count_in)
|
||||
{
|
||||
uint p_next = p_current + 1;
|
||||
if (p_next == count_in)
|
||||
p_next = 0;
|
||||
|
||||
if (ClipValueTest(p_in[p_current][axis], clip)) // Inside.
|
||||
{
|
||||
p_out[count_out++] = p_in[p_current];
|
||||
if (!ClipValueTest(p_in[p_next][axis], clip))
|
||||
__ClipSegment
|
||||
}
|
||||
else // Outside.
|
||||
{
|
||||
if (ClipValueTest(p_in[p_next][axis], clip))
|
||||
__ClipSegment
|
||||
}
|
||||
p_current++;
|
||||
}
|
||||
return count_out;
|
||||
}
|
||||
void Picture::DrawPolygon(uint point_count, Point <float> *point, float r, float g, float b, float a, const Rect <float> *clip_rect)
|
||||
{
|
||||
// Output validity.
|
||||
if (!data || (pxformat.GetBpp() != 32) || pxformat.IsReal())
|
||||
return;
|
||||
|
||||
// Clip primitive.
|
||||
Point <float> *_point = point;
|
||||
|
||||
if (clip_rect)
|
||||
{
|
||||
_point = new Point <float> [128];
|
||||
if (!_point)
|
||||
__ERRRAW__(__LOG_E__ << "failed to allocate polygon clipping array.\n")
|
||||
|
||||
// Old boring clipping code...
|
||||
Point <float> *_point_ = _point + 64;
|
||||
point_count = ClipPolygonAxis(clip_rect->sx, 0, ClipValueTestGreater, point_count, point, _point_);
|
||||
point_count = ClipPolygonAxis(clip_rect->ex, 0, ClipValueTestLess, point_count, _point_, _point);
|
||||
point_count = ClipPolygonAxis(clip_rect->sy, 1, ClipValueTestGreater, point_count, _point, _point_);
|
||||
point_count = ClipPolygonAxis(clip_rect->ey, 1, ClipValueTestLess, point_count, _point_, _point);
|
||||
|
||||
if (point_count < 3)
|
||||
{
|
||||
_safe_delete_array(_point);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Determine entry vertex.
|
||||
float y_scan = _point[0].y,
|
||||
y_max = _point[0].y;
|
||||
int p_int = 0,
|
||||
p_ext = 0;
|
||||
|
||||
for (uint n = 1; n < point_count; ++n)
|
||||
{
|
||||
if (_point[n].y < y_scan)
|
||||
{
|
||||
y_scan = _point[n].y;
|
||||
p_int = p_ext = n;
|
||||
}
|
||||
if (_point[n].y > y_max)
|
||||
y_max = _point[n].y;
|
||||
}
|
||||
|
||||
// Render scanline.
|
||||
float d_int = 0,
|
||||
d_ext = 0;
|
||||
float x_int = _point[p_int].x,
|
||||
x_ext = _point[p_int].x;
|
||||
|
||||
y_scan = Math::Ceil(y_scan);
|
||||
while (y_scan < y_max)
|
||||
{
|
||||
// Update interior pointer.
|
||||
while (y_scan >= _point[p_int].y)
|
||||
{
|
||||
uint p_next = p_int - 1;
|
||||
if (p_next == -1)
|
||||
p_next = point_count - 1;
|
||||
|
||||
// Update delta.
|
||||
d_int = (_point[p_next].x - _point[p_int].x) / (_point[p_next].y - _point[p_int].y);
|
||||
x_int = d_int * (y_scan - _point[p_int].y) + _point[p_int].x;
|
||||
p_int = p_next;
|
||||
}
|
||||
|
||||
// Update exterior pointer.
|
||||
while (y_scan >= _point[p_ext].y)
|
||||
{
|
||||
uint p_next = p_ext + 1;
|
||||
if (p_next == point_count)
|
||||
p_next = 0;
|
||||
|
||||
// Update delta.
|
||||
d_ext = (_point[p_next].x - _point[p_ext].x) / (_point[p_next].y - _point[p_ext].y);
|
||||
x_ext = d_ext * (y_scan - _point[p_ext].y) + _point[p_ext].x;
|
||||
p_ext = p_next;
|
||||
}
|
||||
|
||||
// Draw scanline.
|
||||
{
|
||||
float sx, ex;
|
||||
if (x_int > x_ext)
|
||||
{ sx = x_ext; ex = x_int; }
|
||||
else { sx = x_int; ex = x_ext; }
|
||||
|
||||
for (int x = int(sx); x < int(ex); ++x)
|
||||
DrawPlot((float)x, y_scan, r, g, b, a);
|
||||
}
|
||||
|
||||
// Step scanline boundaries.
|
||||
y_scan += 1;
|
||||
x_int += d_int;
|
||||
x_ext += d_ext;
|
||||
}
|
||||
|
||||
// Release clipping point array.
|
||||
if (clip_rect)
|
||||
_safe_delete_array(_point);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
105
include/framework/picture/pict_fft.cpp
Normal file
105
include/framework/picture/pict_fft.cpp
Normal file
@ -0,0 +1,105 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include <cmath>
|
||||
#include "picture/pict.h"
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void FFT(int size, bool inverse, float *inReal, float *inIm, float *outReal, float *outIm)
|
||||
{
|
||||
// Calculate m = log_2(n).
|
||||
int m = 0, p = 1;
|
||||
for (; p < size; ++m)
|
||||
p *= 2;
|
||||
|
||||
// Bit reversal.
|
||||
outReal[size - 1] = inReal[size - 1];
|
||||
outIm[size - 1] = inIm[size - 1];
|
||||
|
||||
int j = 0;
|
||||
for (int i = 0; i < size - 1; ++i)
|
||||
{
|
||||
outReal[i] = inReal[j];
|
||||
outIm[i] = inIm[j];
|
||||
|
||||
int k = size / 2;
|
||||
while (k <= j)
|
||||
{
|
||||
j -= k;
|
||||
k /= 2;
|
||||
}
|
||||
|
||||
j += k;
|
||||
}
|
||||
|
||||
// Calculate the FFT.
|
||||
float ca = -1.0, sa = 0.0;
|
||||
int l1 = 1, l2 = 1;
|
||||
|
||||
for (int l = 0; l < m; ++l)
|
||||
{
|
||||
l1 = l2;
|
||||
l2 *= 2;
|
||||
|
||||
float u1 = 1.0, u2 = 0.0;
|
||||
|
||||
for(int j = 0; j < l1; j++)
|
||||
{
|
||||
for(int i = j; i < size; i += l2)
|
||||
{
|
||||
int i1 = i + l1;
|
||||
|
||||
float t1 = u1 * outReal[i1] - u2 * outIm[i1],
|
||||
t2 = u1 * outIm[i1] + u2 * outReal[i1];
|
||||
|
||||
outReal[i1] = outReal[i] - t1;
|
||||
outIm[i1] = outIm[i] - t2;
|
||||
outReal[i] += t1;
|
||||
outIm[i] += t2;
|
||||
}
|
||||
|
||||
double z = u1 * ca - u2 * sa;
|
||||
u2 = u1 * sa + u2 * ca;
|
||||
u1 = (float)z;
|
||||
}
|
||||
|
||||
sa = (float)sqrt((1.f - ca) / 2.f);
|
||||
if (!inverse)
|
||||
sa = -sa;
|
||||
ca = (float)sqrt((1.f + ca) / 2.f);
|
||||
}
|
||||
|
||||
// Divide through n if it isn't the IDFT.
|
||||
if (!inverse)
|
||||
for (int i = 0; i < size; ++i)
|
||||
{
|
||||
outReal[i] /= size;
|
||||
outIm[i] /= size;
|
||||
}
|
||||
}
|
||||
|
||||
void testFFT()
|
||||
{
|
||||
float inR[8], inI[8], outR[8], outI[8];
|
||||
|
||||
for (int n = 0; n < 8; ++n)
|
||||
{
|
||||
inR[n] = 5;
|
||||
inI[n] = 2;
|
||||
}
|
||||
|
||||
FFT(8, false, inR, inI, outR, outI);
|
||||
|
||||
for (int n = 0; n < 8; ++n)
|
||||
{
|
||||
inR[n] = 0;
|
||||
inI[n] = 0;
|
||||
}
|
||||
|
||||
FFT(8, true, outR, outI, inR, inI);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
184
include/framework/picture/pict_gradient.cpp
Normal file
184
include/framework/picture/pict_gradient.cpp
Normal file
@ -0,0 +1,184 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "picture/pict_gradient.h"
|
||||
#include "picture/pict.h"
|
||||
#include "metafile/nml.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static uchar ClampChannel(int v)
|
||||
{
|
||||
if (v < 0)
|
||||
return 0;
|
||||
if (v > 255)
|
||||
return 255;
|
||||
return (uchar)v;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Picture::DrawGradient(const Gradient &gradient, const Rect <int> *clip_rect)
|
||||
{
|
||||
if (!gradient.GetControlPointCount())
|
||||
return;
|
||||
|
||||
Rect <int> rect = GetRect();
|
||||
if (!clip_rect)
|
||||
clip_rect = ▭
|
||||
|
||||
uint current_control_point = 0,
|
||||
next_control_point = 1;
|
||||
if (next_control_point == gradient.GetControlPointCount())
|
||||
next_control_point = current_control_point;
|
||||
uchar *pdata = GetDataOffset(clip_rect->sx, clip_rect->sy);
|
||||
|
||||
for (int y = 0; y < clip_rect->GetHeight(); ++y)
|
||||
{
|
||||
float c_k = (float)y / clip_rect->GetHeight();
|
||||
|
||||
// Check gradient interval change.
|
||||
if ((c_k > gradient.k[next_control_point]) && (current_control_point + 1 < gradient.GetControlPointCount()))
|
||||
{
|
||||
current_control_point++;
|
||||
next_control_point++;
|
||||
if (next_control_point == gradient.GetControlPointCount())
|
||||
next_control_point = current_control_point;
|
||||
}
|
||||
|
||||
// Blend.
|
||||
Color blend_color = gradient.color[current_control_point];
|
||||
|
||||
if (current_control_point != next_control_point)
|
||||
blend_color = (gradient.color[next_control_point] - gradient.color[current_control_point]) *
|
||||
(c_k - gradient.k[current_control_point]) / (gradient.k[next_control_point] - gradient.k[current_control_point]) +
|
||||
gradient.color[current_control_point];
|
||||
|
||||
uchar *pscan = pdata,
|
||||
r = (uchar)(blend_color.x * 255),
|
||||
g = (uchar)(blend_color.y * 255),
|
||||
b = (uchar)(blend_color.z * 255),
|
||||
a = (uchar)(blend_color.w * 255);
|
||||
|
||||
switch (gradient.GetOperator())
|
||||
{
|
||||
case Picture::BlendReplace:
|
||||
for (int x = 0; x < rect.GetWidth(); ++x)
|
||||
{
|
||||
pscan[0] = r;
|
||||
pscan[1] = g;
|
||||
pscan[2] = b;
|
||||
pscan[3] = a;
|
||||
pscan += 4;
|
||||
}
|
||||
break;
|
||||
|
||||
case Picture::BlendCompose:
|
||||
for (int x = 0; x < rect.GetWidth(); ++x)
|
||||
{
|
||||
uchar a_blend = AlphaCompositeAlpha(pscan[3], a);
|
||||
pscan[0] = AlphaCompositeColor(pscan[0], r, pscan[3], a, a_blend);
|
||||
pscan[1] = AlphaCompositeColor(pscan[1], g, pscan[3], a, a_blend);
|
||||
pscan[2] = AlphaCompositeColor(pscan[2], b, pscan[3], a, a_blend);
|
||||
pscan[3] = a_blend;
|
||||
pscan += 4;
|
||||
}
|
||||
break;
|
||||
|
||||
case Picture::BlendMultiply:
|
||||
for (int x = 0; x < rect.GetWidth(); ++x)
|
||||
{
|
||||
pscan[0] = (pscan[0] * r) >> 8;
|
||||
pscan[1] = (pscan[1] * g) >> 8;
|
||||
pscan[2] = (pscan[2] * b) >> 8;
|
||||
pscan[3] = (pscan[3] * a) >> 8;
|
||||
pscan += 4;
|
||||
}
|
||||
break;
|
||||
|
||||
case Picture::BlendMultiply2x:
|
||||
for (int x = 0; x < rect.GetWidth(); ++x)
|
||||
{
|
||||
pscan[0] = ClampChannel((pscan[0] * r) >> 7);
|
||||
pscan[1] = ClampChannel((pscan[1] * g) >> 7);
|
||||
pscan[2] = ClampChannel((pscan[2] * b) >> 7);
|
||||
pscan[3] = (pscan[3] * a) >> 8;
|
||||
pscan += 4;
|
||||
}
|
||||
break;
|
||||
|
||||
case Picture::BlendAdd:
|
||||
for (int x = 0; x < rect.GetWidth(); ++x)
|
||||
{
|
||||
pscan[0] = ClampChannel(pscan[0] + r);
|
||||
pscan[1] = ClampChannel(pscan[1] + g);
|
||||
pscan[2] = ClampChannel(pscan[2] + b);
|
||||
pscan[3] = (pscan[3] * a) >> 8;
|
||||
pscan += 4;
|
||||
}
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
pdata += GetPitch();
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Gradient::FromMetaTag(NML::Tag *tag)
|
||||
{
|
||||
control_point_count = 0;
|
||||
op = Picture::BlendMultiply;
|
||||
|
||||
NMLTagForeach(ctag, *tag)
|
||||
{
|
||||
if (ctag->name == "Operator")
|
||||
{
|
||||
String op_string(ctag->GetString());
|
||||
|
||||
if (op_string == "Replace")
|
||||
op = Picture::BlendReplace;
|
||||
else if (op_string == "Multiply")
|
||||
op = Picture::BlendMultiply;
|
||||
else if (op_string == "Multiply2x")
|
||||
op = Picture::BlendMultiply2x;
|
||||
else if (op_string == "Add")
|
||||
op = Picture::BlendAdd;
|
||||
else if (op_string == "Compose")
|
||||
op = Picture::BlendCompose;
|
||||
else __LOG_W__ << "Unknown gradient operator.\n";
|
||||
}
|
||||
else if (ctag->name == "Control")
|
||||
{
|
||||
// Control point count safety.
|
||||
if (control_point_count == 8)
|
||||
__ERR__(__LOG_E__ << "Too many control points in gradient definition.\n", false);
|
||||
|
||||
// Coordinate.
|
||||
NML::Tag *attr_tag = ctag->GetTypedTag("K", Variant::VariantFloat);
|
||||
if (!attr_tag)
|
||||
__ERR__(__LOG_E__ << "Missing tag (control point coordinate) in gradient definition.\n", false);
|
||||
k[control_point_count] = attr_tag->GetReal();
|
||||
|
||||
// Color.
|
||||
attr_tag = ctag->GetTypedTag("Color", Variant::VariantNone);
|
||||
if (!attr_tag)
|
||||
__ERR__(__LOG_E__ << "Missing tag (control point color) in gradient definition.\n", false);
|
||||
color[control_point_count].Set();
|
||||
if (!color[control_point_count].FromMetaTag(*attr_tag))
|
||||
__ERR__(__LOG_E__ << "Erroneous control point color in gradient definition.\n", false);
|
||||
|
||||
control_point_count++;
|
||||
}
|
||||
else __ERR__(__LOG_E__ << "Unexpected tag in gradient definition.\n", false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
70
include/framework/picture/pict_io.cpp
Normal file
70
include/framework/picture/pict_io.cpp
Normal file
@ -0,0 +1,70 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "picture/pict_io.h"
|
||||
#include "picture/pict.h"
|
||||
#include "filesystem/io_handle.h"
|
||||
#include "filesystem/filesystem.h"
|
||||
#include "memory/nauto_ptr.h"
|
||||
#include "platform.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::IO;
|
||||
|
||||
template<> PictureIO *Singleton <PictureIO> ::i = NULL;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
PictureCodec *PictureIO::Codec(const char *codec_name)
|
||||
{
|
||||
ListForeachPtr(PictureCodec *, codec, codec_list)
|
||||
if (GS::String(codec->GetName()) == GS::String(codec_name))
|
||||
return codec;
|
||||
return NULL;
|
||||
}
|
||||
bool PictureIO::RegisterCodec(PictureCodec *codec, bool verbose)
|
||||
{
|
||||
if (Codec(codec->GetName()))
|
||||
return false;
|
||||
codec_list.Add(codec);
|
||||
if (verbose)
|
||||
__LOG__ << "Codec '" << codec->GetName() << "' registered successfully.\n";
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool PictureIO::Save(const Picture &picture, const char *uri, const char *codec_name)
|
||||
{
|
||||
if (PictureCodec *c = Codec(codec_name))
|
||||
{
|
||||
AutoPtr <Handle> h(Platform::Get().io->Open(uri, ModeWrite));
|
||||
if (h.IsNull())
|
||||
return false;
|
||||
|
||||
if (!c->Save(*h, picture))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
bool PictureIO::Load(Picture &picture, const char *uri)
|
||||
{
|
||||
AutoPtr <Handle> h(Platform::Get().io->Open(uri));
|
||||
if (h.IsNull())
|
||||
return false;
|
||||
|
||||
picture.name = uri;
|
||||
ListForeachPtr(PictureCodec *, codec, codec_list)
|
||||
if (codec->Load(*h, picture))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
183
include/framework/picture/pict_sampling.cpp
Normal file
183
include/framework/picture/pict_sampling.cpp
Normal file
@ -0,0 +1,183 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "picture/pict.h"
|
||||
#include "color/color.h"
|
||||
|
||||
using namespace GS;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Picture::Sample(float u, float v, Color &out, uint _w, uint _h) const
|
||||
{
|
||||
if (!GetData())
|
||||
{
|
||||
out.Set(1, 0, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!GetPixelFormat().IsReal())
|
||||
{
|
||||
uint _out;
|
||||
Sample(u, v, _out, _w, _h);
|
||||
out.FromInteger(_out);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!_w) _w = GetWidth();
|
||||
if (!_h) _h = GetHeight();
|
||||
u *= _w; v *= _h;
|
||||
|
||||
if (u < 0) u = 0;
|
||||
if (v < 0) v = 0;
|
||||
if (u >= _w) u = (float)(_w - 1);
|
||||
if (v >= _h) v = (float)(_h - 1);
|
||||
|
||||
uint iu = (uint)u, iv = (uint)v;
|
||||
|
||||
Color sample[4];
|
||||
|
||||
float *pdata = (float *)GetData();
|
||||
pdata += (iu + iv * _w) * 3;
|
||||
|
||||
//---------------------------------------
|
||||
#define EXTRACT_FSAMPLE(_S_, _P_) \
|
||||
{ \
|
||||
(_S_).z = (_P_)[2]; \
|
||||
(_S_).y = (_P_)[1]; \
|
||||
(_S_).x = (_P_)[0]; \
|
||||
}
|
||||
//---------------------------------------
|
||||
|
||||
EXTRACT_FSAMPLE(sample[0], pdata + 0);
|
||||
if (iu < (_w - 1))
|
||||
EXTRACT_FSAMPLE(sample[1], pdata + 3)
|
||||
else sample[1] = sample[0];
|
||||
if (iv < (_h - 1))
|
||||
EXTRACT_FSAMPLE(sample[2], pdata + _w * 3)
|
||||
else sample[2] = sample[0];
|
||||
if ((iu < (_w - 1)) && (iv < (_h - 1)))
|
||||
EXTRACT_FSAMPLE(sample[3], pdata + (_w + 1) * 3)
|
||||
else sample[3] = sample[0];
|
||||
|
||||
// Bilinear sample.
|
||||
float k[4], uf = u - (float)iu, vf = v - (float)iv;
|
||||
|
||||
k[0] = (1.f - uf) * (1.f - vf);
|
||||
k[1] = uf * (1.f - vf);
|
||||
k[2] = (1.f - uf) * vf;
|
||||
k[3] = uf * vf;
|
||||
|
||||
out = sample[0] * k[0] + sample[1] * k[1] + sample[2] * k[2] + sample[3] * k[3];
|
||||
out.w = sample[0].w * k[0] + sample[1].w * k[1] + sample[2].w * k[2] + sample[3].w * k[3];
|
||||
}
|
||||
}
|
||||
void Picture::Sample(float u, float v, uint &out, uint _w, uint _h) const
|
||||
{
|
||||
if (!GetData())
|
||||
{
|
||||
out = 0xffff00ff;
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetPixelFormat().IsReal())
|
||||
{
|
||||
Color _out;
|
||||
Sample(u, v, _out, _w, _h);
|
||||
out = _out.AsInteger();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!_w) _w = GetWidth();
|
||||
if (!_h) _h = GetHeight();
|
||||
u *= _w; v *= _h;
|
||||
|
||||
if (u < 0) u = 0;
|
||||
if (v < 0) v = 0;
|
||||
if (u >= _w) u = (float)(_w - 1);
|
||||
if (v >= _h) v = (float)(_h - 1);
|
||||
|
||||
uint iu = (uint)u, iv = (uint)v;
|
||||
|
||||
struct iVector
|
||||
{
|
||||
int x, y, z, w;
|
||||
|
||||
iVector operator + (const iVector &b) const
|
||||
{ return iVector(x + b.x, y + b.y, z + b.z, w + b.w); }
|
||||
iVector operator * (const int v) const
|
||||
{ return iVector(x * v, y * v, z * v, w * v); }
|
||||
iVector operator >> (const int v) const
|
||||
{ return iVector(x >> v, y >> v, z >> v, w >> v); }
|
||||
|
||||
iVector(int _x, int _y, int _z, int _w = 255)
|
||||
{ x = _x; y = _y; z = _z; w = _w; }
|
||||
iVector()
|
||||
{}
|
||||
};
|
||||
iVector sample[4];
|
||||
|
||||
uint *pdata = (uint *)GetData();
|
||||
pdata += iu + iv * _w;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
#define EXTRACT_SAMPLE(_S_, _P_) \
|
||||
{ \
|
||||
const uchar *_t_p = (const uchar *)&(_P_); \
|
||||
(_S_).x = _t_p[0]; \
|
||||
(_S_).y = _t_p[1]; \
|
||||
(_S_).z = _t_p[2]; \
|
||||
(_S_).w = _t_p[3]; \
|
||||
}
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
EXTRACT_SAMPLE(sample[0], pdata[0]);
|
||||
if (iu < (_w - 1))
|
||||
EXTRACT_SAMPLE(sample[1], pdata[1])
|
||||
else sample[1] = sample[0];
|
||||
if (iv < (_h - 1))
|
||||
EXTRACT_SAMPLE(sample[2], pdata[_w])
|
||||
else sample[2] = sample[0];
|
||||
if ((iu < (_w - 1)) && (iv < (_h - 1)))
|
||||
EXTRACT_SAMPLE(sample[3], pdata[_w + 1])
|
||||
else sample[3] = sample[0];
|
||||
|
||||
int k[4], uf = int((u - (float)iu) * 256), vf = int((v - (float)iv) * 256);
|
||||
|
||||
k[0] = (256 - uf) * (256 - vf); // 16bit fixed point.
|
||||
k[1] = uf * (256 - vf);
|
||||
k[2] = (256 - uf) * vf;
|
||||
k[3] = uf * vf;
|
||||
|
||||
iVector r = (sample[0] * k[0] + sample[1] * k[1] + sample[2] * k[2] + sample[3] * k[3]) >> 16;
|
||||
|
||||
uchar *_out = (uchar *)&out;
|
||||
_out[0] = (uchar)r.x;
|
||||
_out[1] = (uchar)r.y;
|
||||
_out[2] = (uchar)r.z;
|
||||
_out[3] = (uchar)r.w;
|
||||
}
|
||||
}
|
||||
void Picture::SampleRGBA(float u, float v, Color &o, uint _w, uint _h) const
|
||||
{
|
||||
Color s;
|
||||
Sample(u, v, s, _w, _h);
|
||||
|
||||
o[0] = s[pxformat.rshift >> 3];
|
||||
o[1] = s[pxformat.gshift >> 3];
|
||||
o[2] = s[pxformat.bshift >> 3];
|
||||
o[3] = s[pxformat.ashift >> 3];
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
uint Picture::SampleInteger(float u, float v, uint _w, uint _h) const
|
||||
{ uint s; Sample(u, v, s, _w, _h); return s; }
|
||||
Color Picture::SampleColor(float u, float v, uint _w, uint _h) const
|
||||
{ Color s; Sample(u, v, s, _w, _h); return s; }
|
||||
Color Picture::SampleRGBAColor(float u, float v, uint _w, uint _h) const
|
||||
{ Color s; SampleRGBA(u, v, s, _w, _h); return s; }
|
||||
//------------------------------------------------------------------------------
|
||||
206
include/framework/picture/pict_scaler.cpp
Normal file
206
include/framework/picture/pict_scaler.cpp
Normal file
@ -0,0 +1,206 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "picture/pict.h"
|
||||
#include "color/color.h"
|
||||
|
||||
using namespace GS;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Picture::Resize(uint nwidth, uint nheight)
|
||||
{
|
||||
if (!nwidth && !nheight)
|
||||
return false;
|
||||
|
||||
if (!nwidth)
|
||||
nwidth = (GetWidth() * nheight) / GetHeight();
|
||||
if (!nheight)
|
||||
nheight = (GetHeight() * nwidth) / GetWidth();
|
||||
|
||||
if ((nwidth == GetWidth()) && (nheight == GetHeight()))
|
||||
return true;
|
||||
|
||||
if (pxformat.IsReal())
|
||||
{
|
||||
float *new_data = (float *)AllocMemory(sizeof(float) * nwidth * nheight * 3);
|
||||
if (!new_data)
|
||||
return false;
|
||||
|
||||
float *pdst = (float *)new_data;
|
||||
float ku = 1.f / (float)nwidth,
|
||||
kv = 1.f / (float)nheight;
|
||||
|
||||
Color out;
|
||||
|
||||
float v = kv * 0.5f - 0.5f / height;
|
||||
if (data)
|
||||
for (uint y = 0; y < nheight; ++y)
|
||||
{
|
||||
float u = ku * 0.5f - 0.5f / width;
|
||||
for (uint x = 0; x < nwidth; ++x)
|
||||
{
|
||||
Sample(u, v, out);
|
||||
pdst[0] = out.x;
|
||||
pdst[1] = out.y;
|
||||
pdst[2] = out.z;
|
||||
pdst += 3;
|
||||
u += ku;
|
||||
}
|
||||
v += kv;
|
||||
}
|
||||
|
||||
SetData(new_data, nwidth, nheight, PixelFormat::RGBAF, true);
|
||||
//SetData(new_data, nwidth, nheight, PixelFormat::RGBF, true);
|
||||
}
|
||||
else if (GetBpp() == 32)
|
||||
{
|
||||
uchar *new_data = (uchar *)AllocMemory(sizeof(uchar) * nwidth * nheight * 4);
|
||||
if (!new_data)
|
||||
return false;
|
||||
|
||||
uint *pdst = (uint*)new_data;
|
||||
float ku = 1.f / (float)nwidth,
|
||||
kv = 1.f / (float)nheight;
|
||||
|
||||
float v = kv * 0.5f - 0.5f / height;
|
||||
if (data)
|
||||
for (uint y = 0; y < nheight; ++y)
|
||||
{
|
||||
float u = ku * 0.5f - 0.5f / width;
|
||||
for (uint x = 0; x < nwidth; ++x)
|
||||
{
|
||||
Sample(u, v, *pdst++);
|
||||
u += ku;
|
||||
}
|
||||
v += kv;
|
||||
}
|
||||
|
||||
SetData(new_data, nwidth, nheight, GetPixelFormat().GetDesc(), true);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
bool Picture::Downscale(uint nwidth, uint nheight)
|
||||
{
|
||||
if (!nwidth && !nheight)
|
||||
return false;
|
||||
|
||||
if (!nwidth)
|
||||
nwidth = (GetWidth() * nheight) / GetHeight();
|
||||
if (!nheight)
|
||||
nheight = (GetHeight() * nwidth) / GetWidth();
|
||||
|
||||
if ((nwidth > GetWidth()) || (nheight > GetHeight()))
|
||||
return Resize(nwidth, nheight);
|
||||
|
||||
if (GetBpp() == 32)
|
||||
{
|
||||
uchar *new_data = (uchar *)AllocMemory(sizeof(uchar) * nwidth * nheight * 4);
|
||||
if (!new_data)
|
||||
return false;
|
||||
|
||||
uchar *pdst = new_data;
|
||||
Array <float> accu(nwidth * 4);
|
||||
|
||||
if (accu)
|
||||
{
|
||||
float ku = (float)GetWidth() / (float)nwidth,
|
||||
kv = (float)GetHeight() / (float)nheight;
|
||||
float u, v;
|
||||
|
||||
//
|
||||
v = 0.f;
|
||||
|
||||
// Accumulate scan lines.
|
||||
uchar *psrc = GetData();
|
||||
|
||||
for (uint y = 0; y < nheight; y++)
|
||||
{
|
||||
uint n;
|
||||
for (n = 0; n < (nwidth * 4); n++)
|
||||
accu[n] = 0.f;
|
||||
|
||||
float tv = kv, nv = kv;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
float k = GS::Math::Ceil(v) - v;
|
||||
tv -= k;
|
||||
if (tv < 0.f)
|
||||
k += tv; // readjust
|
||||
v += k;
|
||||
|
||||
if (v >= GetHeight())
|
||||
{
|
||||
nv -= k;
|
||||
break;
|
||||
}
|
||||
|
||||
//
|
||||
u = 0.f;
|
||||
|
||||
// Accumulate texels.
|
||||
uchar *lsrc = psrc;
|
||||
float *paccu = accu;
|
||||
float texel[4];
|
||||
|
||||
for (uint x = 0; x < nwidth; x++)
|
||||
{
|
||||
for (n = 0; n < 4; n++)
|
||||
texel[n] = 0.f;
|
||||
|
||||
float tu = ku, nu = ku;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
float k = GS::Math::Ceil(u) - u;
|
||||
tu -= k;
|
||||
if (tu < 0.f)
|
||||
k += tu; // readjust
|
||||
u += k;
|
||||
|
||||
if (u >= GetWidth())
|
||||
{
|
||||
nu -= k;
|
||||
break;
|
||||
}
|
||||
|
||||
for (n = 0; n < 4; n++)
|
||||
texel[n] += (float)(lsrc[n]) * k;
|
||||
|
||||
if (tu < 0.f)
|
||||
break;
|
||||
|
||||
lsrc += 4;
|
||||
}
|
||||
|
||||
tu = k / nu;
|
||||
for (n = 0; n < 4; n++)
|
||||
paccu[n] += texel[n] * tu;
|
||||
paccu += 4;
|
||||
}
|
||||
if (tv < 0.f)
|
||||
break;
|
||||
|
||||
psrc += GetWidth() * 4;
|
||||
}
|
||||
|
||||
tv = 1.f / nv;
|
||||
for (n = 0; n < (nwidth * 4); n++)
|
||||
pdst[n] = (uchar)(accu[n] * tv);
|
||||
pdst += nwidth * 4;
|
||||
}
|
||||
|
||||
SetData(new_data, nwidth, nheight, GetPixelFormat().GetDesc(), true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
2644
include/framework/picture/pict_tga.cpp
Normal file
2644
include/framework/picture/pict_tga.cpp
Normal file
File diff suppressed because it is too large
Load Diff
122
include/framework/picture/pict_tools.cpp
Normal file
122
include/framework/picture/pict_tools.cpp
Normal file
@ -0,0 +1,122 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
------------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
#include "picture/pict_tools.h"
|
||||
#include "picture/pict.h"
|
||||
|
||||
using namespace GS;
|
||||
|
||||
|
||||
namespace GS {
|
||||
namespace PictureTools {
|
||||
|
||||
bool Compare(const Picture &a, const Picture &b, float threshold)
|
||||
{
|
||||
if ((a.GetWidth() != b.GetWidth()) || (a.GetHeight() != b.GetHeight()))
|
||||
return false;
|
||||
|
||||
float dt = 0.f;
|
||||
for (uint y = 0; y < a.GetHeight(); ++y)
|
||||
for (uint x = 0; x < a.GetWidth(); ++x)
|
||||
for (int c = 0; c < 4; ++c)
|
||||
dt += (a.GetDataOffset(x, y)[c] - b.GetDataOffset(x, y)[c]) / 255.f;
|
||||
|
||||
return asbool(dt <= threshold);
|
||||
}
|
||||
|
||||
} // PictureTools
|
||||
} // GS
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Picture::Fill(float r, float g, float b, float a, const iRect *rect, bool lock_alpha)
|
||||
{
|
||||
uint *data = (uint *)GetData();
|
||||
|
||||
if (GetBpp() != 32)
|
||||
return false;
|
||||
if (!data)
|
||||
return false;
|
||||
|
||||
// Convert color.
|
||||
uint fill = GetPixelFormat().Format(r, g, b, a);
|
||||
|
||||
uchar ur = uchar(r * 255),
|
||||
ug = uchar(g * 255),
|
||||
ub = uchar(b * 255)/*,
|
||||
ua = uchar(a * 255)*/;
|
||||
|
||||
if (rect)
|
||||
{
|
||||
Rect <int> _rect = rect->Intersection(GetRect());
|
||||
|
||||
if ((_rect.sy >= _rect.ey) || (_rect.sx >= _rect.ex))
|
||||
return false;
|
||||
|
||||
data += _rect.sx + _rect.sy * width;
|
||||
for (int y = 0; y < _rect.GetHeight(); ++y)
|
||||
{
|
||||
if (lock_alpha)
|
||||
{
|
||||
uchar *scan = (uchar *)data;
|
||||
for (int x = 0; x < _rect.GetWidth(); ++x)
|
||||
{
|
||||
scan[GetPixelFormat().rshift >> 3] = ur;
|
||||
scan[GetPixelFormat().gshift >> 3] = ug;
|
||||
scan[GetPixelFormat().bshift >> 3] = ub;
|
||||
// scan[GetPixelFormat().ashift >> 3] = ua;
|
||||
scan += 4;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
uint *scan = data;
|
||||
for (int x = 0; x < _rect.GetWidth(); ++x)
|
||||
*scan++ = fill;
|
||||
}
|
||||
data += width;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (lock_alpha)
|
||||
{
|
||||
uchar *scan = (uchar *)data;
|
||||
for (uint n = 0; n < GetWidth() * GetHeight(); n++)
|
||||
{
|
||||
scan[GetPixelFormat().rshift >> 3] = ur;
|
||||
scan[GetPixelFormat().gshift >> 3] = ug;
|
||||
scan[GetPixelFormat().bshift >> 3] = ub;
|
||||
// scan[GetPixelFormat().ashift >> 3] = ua;
|
||||
scan += 4;
|
||||
}
|
||||
}
|
||||
else
|
||||
for (uint n = 0; n < GetWidth() * GetHeight(); n++)
|
||||
data[n] = fill;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Picture::ToGrayscale()
|
||||
{
|
||||
if (!(GetWidth() && GetHeight()))
|
||||
return false;
|
||||
if (GetBpp() != 32)
|
||||
return false;
|
||||
|
||||
uchar *ptr = GetData();
|
||||
for (uint y = 0; y < GetHeight(); y++)
|
||||
for (uint x = 0; x < GetWidth(); x++)
|
||||
{
|
||||
int v = (ptr[0] + ptr[1] + ptr[2]) / 3;
|
||||
ptr[0] = ptr[1] = ptr[2] = (uchar)v;
|
||||
ptr += 4;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
206
include/framework/picture/pict_yuv_tools.cpp
Normal file
206
include/framework/picture/pict_yuv_tools.cpp
Normal file
@ -0,0 +1,206 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "picture/pict.h"
|
||||
#if __PLATFORM_WINDOWS__ && __MMX__
|
||||
#include "mmintrin.h"
|
||||
#endif
|
||||
|
||||
using namespace GS;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static void mmx_yuv2rgb (uchar *py, uchar *pu, uchar *pv, uchar *image)
|
||||
{
|
||||
#if __PLATFORM_WINDOWS__ && __MMX__
|
||||
static __m64 mmx_80w = {0x0080008000800080LL};
|
||||
static __m64 mmx_U_green = {0xf37df37df37df37dLL};
|
||||
static __m64 mmx_U_blue = {0x4093409340934093LL};
|
||||
static __m64 mmx_V_red = {0x3312331233123312LL};
|
||||
static __m64 mmx_V_green = {0xe5fce5fce5fce5fcLL};
|
||||
static __m64 mmx_10w = {0x1010101010101010LL};
|
||||
static __m64 mmx_00ffw = {0x00ff00ff00ff00ffLL};
|
||||
static __m64 mmx_Y_coeff = {0x253f253f253f253fLL};
|
||||
static __m64 mmx_A_unpack = {0xffffffffffffffffLL};
|
||||
|
||||
__asm
|
||||
{
|
||||
push esi
|
||||
pxor mm4, mm4 ; mm4 = 0
|
||||
|
||||
mov esi, pu
|
||||
movd mm0, [esi] ; mm0 = 00 00 00 00 u3 u2 u1 u0
|
||||
mov esi, pv
|
||||
movd mm1, [esi] ; mm1 = 00 00 00 00 v3 v2 v1 v0
|
||||
mov esi, py
|
||||
movq mm6, [esi] ; mm6 = Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0
|
||||
|
||||
; Multiply part of the conversion.
|
||||
punpcklbw mm0, mm4 ; mm0 = u3 u2 u1 u0
|
||||
punpcklbw mm1, mm4 ; mm1 = v3 v2 v1 v0
|
||||
psubsw mm0, mmx_80w ; u -= 128
|
||||
psubsw mm1, mmx_80w ; v -= 128
|
||||
psllw mm0, 3 ; promote precision
|
||||
psllw mm1, 3 ; promote precision
|
||||
movq mm2, mm0 ; mm2 = u3 u2 u1 u0
|
||||
movq mm3, mm1 ; mm3 = v3 v2 v1 v0
|
||||
pmulhw mm2, mmx_U_green ; mm2 = u * u_green
|
||||
pmulhw mm3, mmx_V_green ; mm3 = v * v_green
|
||||
pmulhw mm0, mmx_U_blue ; mm0 = chroma_b
|
||||
pmulhw mm1, mmx_V_red ; mm1 = chroma_r
|
||||
paddsw mm2, mm3 ; mm2 = chroma_g
|
||||
|
||||
psubusb mm6, mmx_10w ; Y -= 16
|
||||
movq mm7, mm6 ; mm7 = Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0
|
||||
pand mm6, mmx_00ffw ; mm6 = Y6 Y4 Y2 Y0
|
||||
psrlw mm7, 8 ; mm7 = Y7 Y5 Y3 Y1
|
||||
psllw mm6, 3 ; promote precision
|
||||
psllw mm7, 3 ; promote precision
|
||||
pmulhw mm6, mmx_Y_coeff ; mm6 = luma_rgb even
|
||||
pmulhw mm7, mmx_Y_coeff ; mm7 = luma_rgb odd
|
||||
|
||||
; Addition part of the conversion for even and odd pixels.
|
||||
movq mm3, mm0 ; mm3 = chroma_b
|
||||
movq mm4, mm1 ; mm4 = chroma_r
|
||||
movq mm5, mm2 ; mm5 = chroma_g
|
||||
paddsw mm0, mm6 ; mm0 = B6 B4 B2 B0
|
||||
paddsw mm3, mm7 ; mm3 = B7 B5 B3 B1
|
||||
paddsw mm1, mm6 ; mm1 = R6 R4 R2 R0
|
||||
paddsw mm4, mm7 ; mm4 = R7 R5 R3 R1
|
||||
paddsw mm2, mm6 ; mm2 = G6 G4 G2 G0
|
||||
paddsw mm5, mm7 ; mm5 = G7 G5 G3 G1
|
||||
packuswb mm0, mm0 ; saturate to 0-255
|
||||
packuswb mm1, mm1 ; saturate to 0-255
|
||||
packuswb mm2, mm2 ; saturate to 0-255
|
||||
packuswb mm3, mm3 ; saturate to 0-255
|
||||
packuswb mm4, mm4 ; saturate to 0-255
|
||||
packuswb mm5, mm5 ; saturate to 0-255
|
||||
punpcklbw mm0, mm3 ; mm0 = B7 B6 B5 B4 B3 B2 B1 B0
|
||||
punpcklbw mm1, mm4 ; mm1 = R7 R6 R5 R4 R3 R2 R1 R0
|
||||
punpcklbw mm2, mm5 ; mm2 = G7 G6 G5 G4 G3 G2 G1 G0
|
||||
|
||||
mov esi, image
|
||||
;pxor mm3, mm3
|
||||
movq mm3, mmx_A_unpack
|
||||
movq mm6, mm0
|
||||
movq mm7, mm1
|
||||
movq mm4, mm0
|
||||
movq mm5, mm1
|
||||
punpcklbw mm6, mm2
|
||||
punpcklbw mm7, mm3
|
||||
punpcklwd mm6, mm7
|
||||
movq [esi], mm6
|
||||
movq mm6, mm0
|
||||
punpcklbw mm6, mm2
|
||||
punpckhwd mm6, mm7
|
||||
movq [esi + 8], mm6
|
||||
punpckhbw mm4, mm2
|
||||
punpckhbw mm5, mm3
|
||||
punpcklwd mm4, mm5
|
||||
movq [esi + 16], mm4
|
||||
movq mm4, mm0
|
||||
punpckhbw mm4, mm2
|
||||
punpckhwd mm4, mm5
|
||||
movq [esi + 24], mm4
|
||||
pop esi
|
||||
|
||||
emms
|
||||
}
|
||||
#endif
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Picture::YUV422toRGB32(uchar * const yuv_plane[3], uchar *rgb32, int width, int height, int dst_pitch, int dst_height)
|
||||
{
|
||||
// Default pitch if none specified.
|
||||
if (!dst_pitch)
|
||||
dst_pitch = width * 4;
|
||||
if (!dst_height)
|
||||
dst_height = height;
|
||||
|
||||
// Target resolution.
|
||||
int c_width = dst_pitch / 4;
|
||||
|
||||
if (width < c_width)
|
||||
c_width = width;
|
||||
if (height < dst_height)
|
||||
dst_height = height;
|
||||
|
||||
#pragma omp parallel for
|
||||
for (int y = 0; y < dst_height; ++y)
|
||||
{
|
||||
uchar *p_out = rgb32 + dst_pitch * y,
|
||||
*p_yuv[3] = {
|
||||
yuv_plane[0] + width * y,
|
||||
yuv_plane[1] + (width / 2) * (y / 2),
|
||||
yuv_plane[2] + (width / 2) * (y / 2)
|
||||
};
|
||||
|
||||
// Convert scan line.
|
||||
for (int x = 0; x < c_width; x += 8)
|
||||
{
|
||||
mmx_yuv2rgb(p_yuv[0], p_yuv[1], p_yuv[2], p_out);
|
||||
|
||||
p_yuv[0] += 8;
|
||||
p_yuv[1] += 4;
|
||||
p_yuv[2] += 4;
|
||||
p_out += 32;
|
||||
}
|
||||
}
|
||||
}
|
||||
void Picture::UnpackYCbCr(uchar *y, uchar *cb, uchar *cr)
|
||||
{
|
||||
if (!y || !cb || !cr || (GetBpp() != 32))
|
||||
return;
|
||||
|
||||
uchar *pdata = GetData();
|
||||
for (uint n = 0; n < (GetHeight() * GetWidth()); ++n)
|
||||
{
|
||||
float r = (float)pdata[0], g = (float)pdata[1], b = (float)pdata[2];
|
||||
|
||||
*y++ = (uchar)( 0.2990f * r + 0.5870f * g + 0.1140f * b + 0.5f);
|
||||
*cb++ = (uchar)(-0.1687f * r - 0.3313f * g + 0.5000f * b + 128.f + 0.5f);
|
||||
*cr++ = (uchar)( 0.5000f * r - 0.4187f * g - 0.0813f * b + 128.f + 0.5f);
|
||||
pdata += 4;
|
||||
}
|
||||
}
|
||||
void Picture::PackYCbCr(uchar *y, uchar *cb, uchar *cr)
|
||||
{
|
||||
if (!y || !cb || !cr || (GetBpp() != 32))
|
||||
return;
|
||||
|
||||
uchar *pdata = GetData();
|
||||
for (uint n = 0; n < (GetHeight() * GetWidth()); ++n)
|
||||
{
|
||||
float _y = (float)*y++, _cb = ((float)*cb++) - 128.f, _cr = ((float)*cr++) - 128.f;
|
||||
|
||||
float r = _y + 1.402f * _cr;
|
||||
float g = _y - 0.34414f * _cb - 0.71414f * _cr;
|
||||
float b = _y + 1.77200f * _cb;
|
||||
|
||||
if (r < 0)
|
||||
pdata[0] = 0;
|
||||
else if (r > 255.f)
|
||||
pdata[0] = 255;
|
||||
else pdata[0] = (uchar)r;
|
||||
|
||||
if (g < 0)
|
||||
pdata[1] = 0;
|
||||
else if (g > 255.f)
|
||||
pdata[1] = 255;
|
||||
else pdata[1] = (uchar)g;
|
||||
|
||||
if (b < 0)
|
||||
pdata[2] = 0;
|
||||
else if (b > 255.f)
|
||||
pdata[2] = 255;
|
||||
else pdata[2] = (uchar)b;
|
||||
|
||||
pdata += 4;
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
31
include/framework/plugin/shared_systems.cpp
Normal file
31
include/framework/plugin/shared_systems.cpp
Normal file
@ -0,0 +1,31 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "plugin/shared_systems.h"
|
||||
#include "picture/pict_io.h"
|
||||
#include "audio/audio_io.h"
|
||||
#include "platform.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void SharedSystems::Get()
|
||||
{
|
||||
platform = &Platform::Get();
|
||||
log_system = &LogSystem::Get();
|
||||
picture_io = &PictureIO::Get();
|
||||
audio_io = &AudioIO::Get();
|
||||
}
|
||||
void SharedSystems::Set()
|
||||
{
|
||||
Platform::Set(platform);
|
||||
LogSystem::Set(log_system);
|
||||
PictureIO::Set(picture_io);
|
||||
AudioIO::Set(audio_io);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
7
include/framework/sort/sort.cpp
Normal file
7
include/framework/sort/sort.cpp
Normal file
@ -0,0 +1,7 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "sort/sort.h"
|
||||
39
include/framework/timing/benchmark.cpp
Normal file
39
include/framework/timing/benchmark.cpp
Normal file
@ -0,0 +1,39 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "timing/benchmark.h"
|
||||
#include "sort/sort.h"
|
||||
#include "platform.h"
|
||||
|
||||
using namespace GS;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Benchmark::Start()
|
||||
{
|
||||
r_clock = Platform::Get().GetTime();
|
||||
}
|
||||
void Benchmark::Stop()
|
||||
{
|
||||
Time c_clock = Platform::Get().GetTime();
|
||||
|
||||
t_clock += c_clock - r_clock;
|
||||
r_clock = c_clock;
|
||||
}
|
||||
float Benchmark::GetMs() const
|
||||
{ return avg.GetMedian(); }
|
||||
void Benchmark::Reset()
|
||||
{
|
||||
avg.LogValue(t_clock.toMs());
|
||||
t_clock.setSec(0);
|
||||
}
|
||||
|
||||
Benchmark::Benchmark(bool start)
|
||||
{
|
||||
if (start)
|
||||
Start();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
53
include/framework/timing/loop_benchmark.cpp
Normal file
53
include/framework/timing/loop_benchmark.cpp
Normal file
@ -0,0 +1,53 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "timing/loop_benchmark.h"
|
||||
#include "platform.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void LoopBenchmark::MarkLoop()
|
||||
{
|
||||
++loop_count;
|
||||
|
||||
Time c_time = Platform::Get().GetTime();
|
||||
|
||||
t_time += c_time - r_time;
|
||||
r_time = c_time;
|
||||
|
||||
float t_ms = t_time.toMs();
|
||||
|
||||
if (t_ms > 1000.f)
|
||||
{
|
||||
ms = t_ms / loop_count;
|
||||
|
||||
loop_count = 0;
|
||||
t_time.setSec(0);
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
float LoopBenchmark::GetMs() const
|
||||
{ return ms; }
|
||||
float LoopBenchmark::GetFps() const
|
||||
{ return ms ? 1000.f / ms : 0.f; }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void LoopBenchmark::Reset()
|
||||
{
|
||||
ms = 0.f;
|
||||
|
||||
loop_count = 0;
|
||||
|
||||
r_time = Platform::Get().GetTime();
|
||||
t_time.setSec(0);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
Reference in New Issue
Block a user