// // Lol Engine // // Copyright: (c) 2010-2013 Sam Hocevar <sam@hocevar.net> // This program is free software; you can redistribute it and/or // modify it under the terms of the Do What The Fuck You Want To // Public License, Version 2, as published by Sam Hocevar. See // http://www.wtfpl.net/ for more details. // #if defined HAVE_CONFIG_H # include "config.h" #endif #include <lol/main.h> #include "lolgl.h" #if defined _WIN32 && defined USE_D3D9 # define FAR # define NEAR # include <d3d9.h> #elif defined _XBOX # include <xtl.h> # undef near /* Fuck Microsoft */ # undef far /* Fuck Microsoft again */ #endif using namespace std; namespace lol { // // The VertexBufferData class // -------------------------- // class VertexBufferData { friend class VertexBuffer; friend class VertexDeclaration; size_t m_size; #if defined USE_D3D9 IDirect3DDevice9 *m_dev; IDirect3DVertexBuffer9 *m_vbo; #elif defined _XBOX D3DDevice *m_dev; D3DVertexBuffer *m_vbo; #else GLuint m_vbo; uint8_t *m_memory; #endif }; // // The VertexDeclarationData class // ------------------------------- // class VertexDeclarationData { friend class VertexBuffer; friend class VertexDeclaration; #if defined USE_D3D9 IDirect3DDevice9 *m_dev; IDirect3DVertexDeclaration9 *m_vdecl; #elif defined _XBOX D3DDevice *m_dev; D3DVertexDeclaration *m_vdecl; #else #endif }; // // The VertexDeclaration class // --------------------------- // VertexStreamBase const VertexStreamBase::Empty; VertexDeclaration::VertexDeclaration(VertexStreamBase const &s1, VertexStreamBase const &s2, VertexStreamBase const &s3, VertexStreamBase const &s4, VertexStreamBase const &s5, VertexStreamBase const &s6, VertexStreamBase const &s7, VertexStreamBase const &s8, VertexStreamBase const &s9, VertexStreamBase const &s10, VertexStreamBase const &s11, VertexStreamBase const &s12) : m_count(0), m_data(new VertexDeclarationData()) { if (&s1 != &VertexStreamBase::Empty) AddStream(s1); if (&s2 != &VertexStreamBase::Empty) AddStream(s2); if (&s3 != &VertexStreamBase::Empty) AddStream(s3); if (&s4 != &VertexStreamBase::Empty) AddStream(s4); if (&s5 != &VertexStreamBase::Empty) AddStream(s5); if (&s6 != &VertexStreamBase::Empty) AddStream(s6); if (&s7 != &VertexStreamBase::Empty) AddStream(s7); if (&s8 != &VertexStreamBase::Empty) AddStream(s8); if (&s9 != &VertexStreamBase::Empty) AddStream(s9); if (&s10 != &VertexStreamBase::Empty) AddStream(s10); if (&s11 != &VertexStreamBase::Empty) AddStream(s11); if (&s12 != &VertexStreamBase::Empty) AddStream(s12); Initialize(); } VertexDeclaration::~VertexDeclaration() { #if defined _XBOX || defined USE_D3D9 if (FAILED(m_data->m_vdecl->Release())) Abort(); #else #endif delete m_data; } void VertexDeclaration::Bind() { #if defined _XBOX || defined USE_D3D9 if (FAILED(m_data->m_dev->SetVertexDeclaration(m_data->m_vdecl))) Abort(); #else /* FIXME: Nothing to do? */ #endif } void VertexDeclaration::DrawElements(MeshPrimitive type, int skip, int count) { if (count <= 0) return; #if defined _XBOX || defined USE_D3D9 switch (type.ToScalar()) { case MeshPrimitive::Triangles: if (FAILED(m_data->m_dev->DrawPrimitive(D3DPT_TRIANGLELIST, skip, count))) Abort(); break; case MeshPrimitive::TriangleStrips: if (FAILED(m_data->m_dev->DrawPrimitive(D3DPT_TRIANGLESTRIP, skip, count))) Abort(); break; case MeshPrimitive::TriangleFans: if (FAILED(m_data->m_dev->DrawPrimitive(D3DPT_TRIANGLEFAN, skip, count))) Abort(); break; case MeshPrimitive::Points: if (FAILED(m_data->m_dev->DrawPrimitive(D3DPT_POINTLIST, skip, count))) Abort(); break; case MeshPrimitive::Lines: if (FAILED(m_data->m_dev->DrawPrimitive(D3DPT_LINELIST, skip, count))) Abort(); break; } #else /* FIXME: this has nothing to do here! */ switch (type.ToScalar()) { case MeshPrimitive::Triangles: glDrawArrays(GL_TRIANGLES, skip, count); break; case MeshPrimitive::TriangleStrips: glDrawArrays(GL_TRIANGLE_STRIP, skip, count); break; case MeshPrimitive::TriangleFans: glDrawArrays(GL_TRIANGLE_FAN, skip, count); break; case MeshPrimitive::Points: glDrawArrays(GL_POINTS, skip, count); break; case MeshPrimitive::Lines: glDrawArrays(GL_LINES, skip, count); break; } #endif } void VertexDeclaration::DrawIndexedElements(MeshPrimitive type, int vbase, int vskip, int vcount, int skip, int count) { if (count <= 0) return; #if defined _XBOX || defined USE_D3D9 switch (type.ToScalar()) { case MeshPrimitive::Triangles: count = count / 3; if (FAILED(m_data->m_dev->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, vbase, vskip, vcount, skip, count))) Abort(); break; case MeshPrimitive::TriangleStrips: count = count - 2; if (FAILED(m_data->m_dev->DrawIndexedPrimitive(D3DPT_TRIANGLESTRIP, vbase, vskip, vcount, skip, count))) Abort(); break; case MeshPrimitive::TriangleFans: count = count - 2; if (FAILED(m_data->m_dev->DrawIndexedPrimitive(D3DPT_TRIANGLEFAN, vbase, vskip, vcount, skip, count))) Abort(); break; case MeshPrimitive::Points: if (FAILED(m_data->m_dev->DrawIndexedPrimitive(D3DPT_POINTLIST, vbase, vskip, vcount, skip, count))) Abort(); break; case MeshPrimitive::Lines: if (FAILED(m_data->m_dev->DrawIndexedPrimitive(D3DPT_LINELIST, vbase, vskip, vcount, skip, count))) Abort(); break; } #else /* FIXME: this has nothing to do here! */ switch (type.ToScalar()) { case MeshPrimitive::Triangles: /* FIXME: ignores most of the arguments! */ UNUSED(vbase, vskip, vcount, skip); glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, 0); break; case MeshPrimitive::TriangleStrips: /* FIXME: ignores most of the arguments! */ UNUSED(vbase, vskip, vcount, skip); glDrawElements(GL_TRIANGLE_STRIP, count, GL_UNSIGNED_SHORT, 0); break; case MeshPrimitive::TriangleFans: /* FIXME: ignores most of the arguments! */ UNUSED(vbase, vskip, vcount, skip); glDrawElements(GL_TRIANGLE_FAN, count, GL_UNSIGNED_SHORT, 0); break; case MeshPrimitive::Points: /* FIXME: ignores most of the arguments! */ UNUSED(vbase, vskip, vcount, skip); glDrawElements(GL_POINTS, count, GL_UNSIGNED_SHORT, 0); break; case MeshPrimitive::Lines: /* FIXME: ignores most of the arguments! */ UNUSED(vbase, vskip, vcount, skip); glDrawElements(GL_LINES, count, GL_UNSIGNED_SHORT, 0); break; } #endif } void VertexDeclaration::Unbind() { #if defined _XBOX || defined USE_D3D9 int stream = -1; for (int i = 0; i < m_count; i++) if (m_streams[i].index != stream) { stream = m_streams[i].index; if (FAILED(m_data->m_dev->SetStreamSource(stream, 0, 0, 0))) Abort(); } /* "NULL is an invalid input to SetVertexDeclaration" (DX9 guide), so * we just don't touch the current vertex declaration. */ #elif !defined __CELLOS_LV2__ for (int i = 0; i < m_count; i++) { if (m_streams[i].reg >= 0) { for (int j = i + 1; j < m_count; j++) if (m_streams[j].reg == m_streams[i].reg) m_streams[j].reg = -1; glDisableVertexAttribArray(m_streams[i].reg); } } glBindBuffer(GL_ARRAY_BUFFER, 0); #else /* Or even: */ glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_COLOR_ARRAY); #endif } void VertexDeclaration::SetStream(VertexBuffer *vb, ShaderAttrib attr1, ShaderAttrib attr2, ShaderAttrib attr3, ShaderAttrib attr4, ShaderAttrib attr5, ShaderAttrib attr6, ShaderAttrib attr7, ShaderAttrib attr8, ShaderAttrib attr9, ShaderAttrib attr10, ShaderAttrib attr11, ShaderAttrib attr12) { #if defined _XBOX || defined USE_D3D9 // Don't bother in DirectX world, shader attributes are not used SetStream(vb, NULL); #else ShaderAttrib attribs[12] = { attr1, attr2, attr3, attr4, attr5, attr6, attr7, attr8, attr9, attr10, attr11, attr12 }; SetStream(vb, attribs); #endif } void VertexDeclaration::SetStream(VertexBuffer *vb, ShaderAttrib attribs[]) { if (!vb->m_data->m_size) return; #if defined _XBOX || defined USE_D3D9 /* Only the first item is required to know which stream this * is about; the rest of the information is stored in the * vertex declaration already. */ VertexUsage usage = VertexUsage((attr1.m_flags >> 16) & 0xffff); uint32_t index = attr1.m_flags & 0xffff; /* Find the stream number */ uint32_t usage_index = 0; int stream = -1; for (int i = 0; i < m_count; i++) if (m_streams[i].usage == usage) if (usage_index++ == index) { stream = m_streams[i].index; break; } /* Compute this stream's stride */ int stride = 0; for (int i = 0; i < m_count; i++) if (stream == m_streams[i].index) stride += m_streams[i].size; /* Now we know the stream index and the element stride */ /* FIXME: precompute most of the crap above! */ if (stream >= 0) { if (FAILED(m_data->m_dev->SetStreamSource(stream, vb->m_data->m_vbo, 0, stride))) Abort(); } #else glBindBuffer(GL_ARRAY_BUFFER, vb->m_data->m_vbo); for (int n = 0; n < 12 && attribs[n].m_flags != (uint64_t)0 - 1; n++) { VertexUsage usage = VertexUsage((attribs[n].m_flags >> 16) & 0xffff); uint32_t index = attribs[n].m_flags & 0xffff; uint32_t reg = attribs[n].m_flags >> 32; # if !defined __CELLOS_LV2__ if (reg != 0xffffffffu) glEnableVertexAttribArray((GLint)reg); # else switch (usage.ToScalar()) { case VertexUsage::Position: glEnableClientState(GL_VERTEX_ARRAY); break; case VertexUsage::TexCoord: case VertexUsage::TexCoordExt: glEnableClientState(GL_TEXTURE_COORD_ARRAY); break; case VertexUsage::Normal: glEnableClientState(GL_NORMAL_ARRAY); break; case VertexUsage::Color: glEnableClientState(GL_COLOR_ARRAY); break; } # endif /* We need to parse the whole vertex declaration to retrieve * the information. It sucks. */ int attr_index = 0; /* First, find the stream index */ for (uint32_t usage_index = 0; attr_index < m_count; attr_index++) if (m_streams[attr_index].usage == usage) if (usage_index++ == index) break; if (attr_index == m_count) { Log::Error("stream #%d with usage %x not found in declaration\n", index, usage); attr_index = 0; } /* Now compute the stride and offset up to this stream index */ int stride = 0, offset = 0; for (int i = 0; i < m_count; i++) if (m_streams[i].index == m_streams[attr_index].index) { /* Remember the register used for this stream */ m_streams[i].reg = reg; stride += m_streams[i].size; if (i < attr_index) offset += m_streams[i].size; } /* Finally, we need to retrieve the type of the data */ # if !defined GL_DOUBLE # define GL_DOUBLE 0 # endif static struct { GLint size; GLenum type; } const tlut[] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, /* half */ { 1, GL_FLOAT }, { 2, GL_FLOAT }, { 3, GL_FLOAT }, { 4, GL_FLOAT }, /* float */ { 1, GL_DOUBLE }, { 2, GL_DOUBLE }, { 3, GL_DOUBLE }, { 4, GL_DOUBLE }, /* double */ { 1, GL_BYTE }, { 2, GL_BYTE }, { 3, GL_BYTE }, { 4, GL_BYTE }, /* int8_t */ { 1, GL_UNSIGNED_BYTE }, { 2, GL_UNSIGNED_BYTE }, { 3, GL_UNSIGNED_BYTE }, { 4, GL_UNSIGNED_BYTE }, /* uint8_t */ { 1, GL_SHORT }, { 2, GL_SHORT }, { 3, GL_SHORT }, { 4, GL_SHORT }, /* int16_t */ { 1, GL_UNSIGNED_SHORT }, { 2, GL_UNSIGNED_SHORT }, { 3, GL_UNSIGNED_SHORT }, { 4, GL_UNSIGNED_SHORT }, /* uint16_t */ { 1, GL_INT }, { 2, GL_INT }, { 3, GL_INT }, { 4, GL_INT }, /* int32_t */ { 1, GL_UNSIGNED_INT }, { 2, GL_UNSIGNED_INT }, { 3, GL_UNSIGNED_INT }, { 4, GL_UNSIGNED_INT }, /* uint32_t */ }; int type_index = m_streams[attr_index].stream_type; if (type_index < 0 || type_index >= (int)(sizeof(tlut) / sizeof(*tlut))) type_index = 0; # if !defined __CELLOS_LV2__ if (reg != 0xffffffff) { if (tlut[type_index].type == GL_FLOAT || tlut[type_index].type == GL_DOUBLE || tlut[type_index].type == GL_BYTE || tlut[type_index].type == GL_UNSIGNED_BYTE # if defined USE_GLEW && !defined __APPLE__ /* If this is not available, don't use it */ || !glVertexAttribIPointer # endif || false) { /* Normalize unsigned bytes by default, because it's usually * some color information. */ GLboolean normalize = (tlut[type_index].type == GL_UNSIGNED_BYTE) || (tlut[type_index].type == GL_BYTE); glVertexAttribPointer((GLint)reg, tlut[type_index].size, tlut[type_index].type, normalize, stride, (GLvoid const *)(uintptr_t)offset); } # if defined GL_VERSION_3_0 else { glVertexAttribIPointer((GLint)reg, tlut[type_index].size, tlut[type_index].type, stride, (GLvoid const *)(uintptr_t)offset); } # endif } # else switch (usage) { case VertexUsage::Position: glVertexPointer(tlut[type_index].size, tlut[type_index].type, stride, (GLvoid const *)(uintptr_t)offset); break; case VertexUsage::TexCoord: case VertexUsage::TexCoordExt: glTexCoordPointer(tlut[type_index].size, tlut[type_index].type, stride, (GLvoid const *)(uintptr_t)offset); break; case VertexUsage::Normal: glNormalPointer(tlut[type_index].type, stride, (GLvoid const *)(uintptr_t)offset); break; case VertexUsage::Color: glColorPointer(tlut[type_index].size, tlut[type_index].type, stride, (GLvoid const *)(uintptr_t)offset); break; default: Log::Error("vertex usage %d is not supported yet\n", usage); break; } # endif } #endif } void VertexDeclaration::Initialize() { #if defined _XBOX || defined USE_D3D9 static D3DVERTEXELEMENT9 const end_element[] = { D3DDECL_END() }; static D3DDECLTYPE const X = D3DDECLTYPE_UNUSED; static D3DDECLTYPE const tlut[] = { D3DDECLTYPE_UNUSED, X, D3DDECLTYPE_FLOAT16_2, X, D3DDECLTYPE_FLOAT16_4, /* half */ D3DDECLTYPE_FLOAT1, D3DDECLTYPE_FLOAT2, D3DDECLTYPE_FLOAT3, D3DDECLTYPE_FLOAT4, /* float */ X, X, X, X, /* double */ X, X, X, X, /* int8_t */ X, X, X, D3DDECLTYPE_UBYTE4N, /* uint8_t */ X, D3DDECLTYPE_SHORT2N, X, D3DDECLTYPE_SHORT4N, /* int16_t */ X, D3DDECLTYPE_USHORT2N, X, D3DDECLTYPE_USHORT4N, /* uint16_t */ X, X, X, X, /* int32_t */ X, X, X, X, /* uint32_t */ }; static D3DDECLUSAGE const ulut[] = { D3DDECLUSAGE_POSITION, D3DDECLUSAGE_BLENDWEIGHT, D3DDECLUSAGE_BLENDINDICES, D3DDECLUSAGE_NORMAL, D3DDECLUSAGE_PSIZE, D3DDECLUSAGE_TEXCOORD, D3DDECLUSAGE_TANGENT, D3DDECLUSAGE_BINORMAL, D3DDECLUSAGE_TESSFACTOR, #if defined _XBOX D3DDECLUSAGE_TEXCOORD, /* FIXME: nonexistent */ #else D3DDECLUSAGE_POSITIONT, #endif D3DDECLUSAGE_COLOR, D3DDECLUSAGE_FOG, D3DDECLUSAGE_DEPTH, D3DDECLUSAGE_SAMPLE, }; D3DVERTEXELEMENT9 elements[12 + 1]; for (int n = 0; n < m_count; n++) { elements[n].Stream = m_streams[n].index; elements[n].Offset = 0; for (int i = 0; i < n; i++) if (m_streams[i].index == m_streams[n].index) elements[n].Offset += m_streams[i].size; if (m_streams[n].stream_type >= 0 && m_streams[n].stream_type < sizeof(tlut) / sizeof(*tlut)) elements[n].Type = tlut[m_streams[n].stream_type]; else elements[n].Type = D3DDECLTYPE_UNUSED; elements[n].Method = D3DDECLMETHOD_DEFAULT; if (m_streams[n].usage >= 0 && m_streams[n].usage < sizeof(ulut) / sizeof(*ulut)) elements[n].Usage = ulut[m_streams[n].usage]; else elements[n].Usage = D3DDECLUSAGE_POSITION; elements[n].UsageIndex = 0; for (int i = 0; i < n; i++) if (elements[i].Stream == elements[n].Stream && elements[i].Usage == elements[n].Usage) elements[n].UsageIndex++; } elements[m_count] = end_element[0]; # if defined USE_D3D9 m_data->m_dev = (IDirect3DDevice9 *)g_renderer->GetDevice(); # elif defined _XBOX m_data->m_dev = (D3DDevice *)g_renderer->GetDevice(); # endif if (FAILED(m_data->m_dev->CreateVertexDeclaration(elements, &m_data->m_vdecl))) Abort(); #else #endif } void VertexDeclaration::AddStream(VertexStreamBase const &s) { int index = m_count ? m_streams[m_count - 1].index + 1 : 0; for (int i = 0; s.m_streams[i].size; i++) { m_streams[m_count].stream_type = s.m_streams[i].stream_type; m_streams[m_count].usage = s.m_streams[i].usage; m_streams[m_count].size = s.m_streams[i].size; m_streams[m_count].index = index; m_streams[m_count].reg = -1; m_count++; } } int VertexDeclaration::GetStreamCount() const { return m_count ? m_streams[m_count - 1].index + 1 : 0; } VertexStreamBase VertexDeclaration::GetStream(int index) const { VertexStreamBase stream; int n = 0; int count = 0; for (int i = 0; i < m_count; ++i) { if (m_streams[i].index != index) continue; switch (m_streams[i].stream_type) { #define __T(T) \ case VertexStreamBase::Type##T: stream.AddStream<T>(n++, m_streams[i].usage); break; __T(void) __T(half) __T(f16vec2) __T(f16vec3) __T(f16vec4) __T(float) __T(vec2) __T(vec3) __T(vec4) __T(double) __T(dvec2) __T(dvec3) __T(dvec4) __T(int8_t) __T(i8vec2) __T(i8vec3) __T(i8vec4) __T(uint8_t) __T(u8vec2) __T(u8vec3) __T(u8vec4) __T(int16_t) __T(i16vec2) __T(i16vec3) __T(i16vec4) __T(uint16_t) __T(u16vec2) __T(u16vec3) __T(u16vec4) __T(int32_t) __T(ivec2) __T(ivec3) __T(ivec4) __T(uint32_t) __T(uvec2) __T(uvec3) __T(uvec4) #undef __T } ++count; } while (count < 12) stream.AddStream<void>(count++, VertexUsage::Position); return stream; } // // The VertexBuffer class // ---------------------- // VertexBuffer::VertexBuffer(size_t size) : m_data(new VertexBufferData) { m_data->m_size = size; if (!size) return; #if defined USE_D3D9 || defined _XBOX # if defined USE_D3D9 m_data->m_dev = (IDirect3DDevice9 *)g_renderer->GetDevice(); # elif defined _XBOX m_data->m_dev = (D3DDevice *)g_renderer->GetDevice(); # endif if (FAILED(m_data->m_dev->CreateVertexBuffer(size, D3DUSAGE_WRITEONLY, nullptr, D3DPOOL_MANAGED, &m_data->m_vbo, nullptr))) Abort(); #else glGenBuffers(1, &m_data->m_vbo); m_data->m_memory = new uint8_t[size]; #endif } VertexBuffer::~VertexBuffer() { if (m_data->m_size) { #if defined USE_D3D9 || defined _XBOX if (FAILED(m_data->m_vbo->Release())) Abort(); #else glDeleteBuffers(1, &m_data->m_vbo); delete[] m_data->m_memory; #endif } delete m_data; } size_t VertexBuffer::GetSize() { return m_data->m_size; } void *VertexBuffer::Lock(size_t offset, size_t size) { if (!m_data->m_size) return nullptr; #if defined USE_D3D9 || defined _XBOX void *ret; if (FAILED(m_data->m_vbo->Lock(offset, size, (void **)&ret, 0))) Abort(); return ret; #else /* FIXME: is there a way to use "size"? */ UNUSED(size); return m_data->m_memory + offset; #endif } void VertexBuffer::Unlock() { if (!m_data->m_size) return; #if defined USE_D3D9 || defined _XBOX if (FAILED(m_data->m_vbo->Unlock())) Abort(); #else glBindBuffer(GL_ARRAY_BUFFER, m_data->m_vbo); glBufferData(GL_ARRAY_BUFFER, m_data->m_size, m_data->m_memory, GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); #endif } } /* namespace lol */