mirror of
https://github.com/love2d/love.git
synced 2026-08-15 07:41:11 +02:00
Merge branch '12.0-development' into metal
This commit is contained in:
@@ -291,6 +291,12 @@ public:
|
||||
**/
|
||||
static bool setMixWithSystem(bool mix);
|
||||
|
||||
/**
|
||||
* Pause/resume audio context
|
||||
*/
|
||||
virtual void pauseContext() = 0;
|
||||
virtual void resumeContext() = 0;
|
||||
|
||||
private:
|
||||
|
||||
static StringMap<DistanceModel, DISTANCE_MAX_ENUM>::Entry distanceModelEntries[];
|
||||
|
||||
@@ -203,6 +203,15 @@ bool Audio::isEFXsupported() const
|
||||
return false;
|
||||
}
|
||||
|
||||
void Audio::pauseContext()
|
||||
{
|
||||
}
|
||||
|
||||
void Audio::resumeContext()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
} // null
|
||||
} // audio
|
||||
} // love
|
||||
|
||||
@@ -86,6 +86,9 @@ public:
|
||||
int getMaxSourceEffects() const;
|
||||
bool isEFXsupported() const;
|
||||
|
||||
void pauseContext();
|
||||
void resumeContext();
|
||||
|
||||
private:
|
||||
float volume;
|
||||
DistanceModel distanceModel;
|
||||
|
||||
@@ -26,6 +26,10 @@
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
|
||||
#ifdef LOVE_IOS
|
||||
#include "common/ios.h"
|
||||
#endif
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace audio
|
||||
@@ -189,10 +193,18 @@ Audio::Audio()
|
||||
|
||||
poolThread = new PoolThread(pool);
|
||||
poolThread->start();
|
||||
|
||||
#ifdef LOVE_IOS
|
||||
love::ios::initAudioSessionInterruptionHandler();
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
Audio::~Audio()
|
||||
{
|
||||
#ifdef LOVE_IOS
|
||||
love::ios::destroyAudioSessionInterruptionHandler();
|
||||
#endif
|
||||
poolThread->setFinish();
|
||||
poolThread->wait();
|
||||
|
||||
@@ -293,6 +305,17 @@ std::vector<love::audio::Source*> Audio::pause()
|
||||
return Source::pause(pool);
|
||||
}
|
||||
|
||||
void Audio::pauseContext()
|
||||
{
|
||||
alcMakeContextCurrent(nullptr);
|
||||
}
|
||||
|
||||
void Audio::resumeContext()
|
||||
{
|
||||
if (context && alcGetCurrentContext() != context)
|
||||
alcMakeContextCurrent(context);
|
||||
}
|
||||
|
||||
void Audio::setVolume(float volume)
|
||||
{
|
||||
alListenerf(AL_GAIN, volume);
|
||||
|
||||
@@ -95,6 +95,8 @@ public:
|
||||
void pause(love::audio::Source *source);
|
||||
void pause(const std::vector<love::audio::Source*> &sources);
|
||||
std::vector<love::audio::Source*> pause();
|
||||
void pauseContext();
|
||||
void resumeContext();
|
||||
void setVolume(float volume);
|
||||
float getVolume() const;
|
||||
|
||||
|
||||
@@ -34,7 +34,6 @@ Buffer::Buffer(Graphics *gfx, const Settings &settings, const std::vector<DataDe
|
||||
, size(size)
|
||||
, typeFlags(settings.typeFlags)
|
||||
, usage(settings.usage)
|
||||
, mapFlags(settings.mapFlags)
|
||||
, mapped(false)
|
||||
{
|
||||
if (size == 0 && arraylength == 0)
|
||||
|
||||
@@ -48,11 +48,9 @@ public:
|
||||
|
||||
static love::Type type;
|
||||
|
||||
enum MapFlags
|
||||
enum MapType
|
||||
{
|
||||
MAP_NONE = 0,
|
||||
MAP_EXPLICIT_RANGE_MODIFY = (1 << 0), // see setMappedRangeModified.
|
||||
MAP_READ = (1 << 1),
|
||||
MAP_WRITE_INVALIDATE,
|
||||
};
|
||||
|
||||
enum TypeFlags
|
||||
@@ -95,13 +93,13 @@ public:
|
||||
struct Settings
|
||||
{
|
||||
TypeFlags typeFlags;
|
||||
MapFlags mapFlags;
|
||||
BufferUsage usage;
|
||||
bool zeroInitialize;
|
||||
|
||||
Settings(uint32 typeflags, uint32 mapflags, BufferUsage usage)
|
||||
Settings(uint32 typeflags, BufferUsage usage)
|
||||
: typeFlags((TypeFlags)typeflags)
|
||||
, mapFlags((MapFlags)mapflags)
|
||||
, usage(usage)
|
||||
, zeroInitialize(false)
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -112,7 +110,6 @@ public:
|
||||
TypeFlags getTypeFlags() const { return typeFlags; }
|
||||
BufferUsage getUsage() const { return usage; }
|
||||
bool isMapped() const { return mapped; }
|
||||
uint32 getMapFlags() const { return mapFlags; }
|
||||
|
||||
size_t getArrayLength() const { return arrayLength; }
|
||||
size_t getArrayStride() const { return arrayStride; }
|
||||
@@ -122,35 +119,21 @@ public:
|
||||
int getDataMemberIndex(const std::string &name) const;
|
||||
|
||||
/**
|
||||
* Map the Buffer to client memory.
|
||||
*
|
||||
* This can be faster for large changes to the buffer. For smaller
|
||||
* changes, see fill().
|
||||
* Map a portion of the Buffer to client memory.
|
||||
*/
|
||||
virtual void *map() = 0;
|
||||
virtual void *map(MapType map, size_t offset, size_t size) = 0;
|
||||
|
||||
/**
|
||||
* Unmap a previously mapped Buffer. The buffer must be unmapped when used
|
||||
* to draw.
|
||||
*/
|
||||
virtual void unmap() = 0;
|
||||
virtual void unmap(size_t usedoffset, size_t usedsize) = 0;
|
||||
|
||||
/**
|
||||
* Marks a range of mapped data as modified.
|
||||
* NOTE: Buffer::fill calls this internally for you.
|
||||
**/
|
||||
virtual void setMappedRangeModified(size_t offset, size_t size) = 0;
|
||||
|
||||
/**
|
||||
* Fill a portion of the buffer with data and marks the range as modified.
|
||||
* Fill a portion of the buffer with data.
|
||||
*/
|
||||
virtual void fill(size_t offset, size_t size, const void *data) = 0;
|
||||
|
||||
/**
|
||||
* Copy the contents of this Buffer to another Buffer object.
|
||||
**/
|
||||
virtual void copyTo(size_t offset, size_t size, Buffer *other, size_t otheroffset) = 0;
|
||||
|
||||
/**
|
||||
* Texel buffers may use an additional texture handle as well as a buffer
|
||||
* handle.
|
||||
@@ -166,14 +149,12 @@ public:
|
||||
Mapper(Buffer &buffer)
|
||||
: buffer(buffer)
|
||||
{
|
||||
data = buffer.map();
|
||||
data = buffer.map(MAP_WRITE_INVALIDATE, 0, buffer.getSize());
|
||||
}
|
||||
|
||||
~Mapper()
|
||||
{
|
||||
if (buffer.getMapFlags() & MAP_EXPLICIT_RANGE_MODIFY)
|
||||
buffer.setMappedRangeModified(0, buffer.getSize());
|
||||
buffer.unmap();
|
||||
buffer.unmap(0, buffer.getSize());
|
||||
}
|
||||
|
||||
Buffer &buffer;
|
||||
@@ -195,8 +176,6 @@ protected:
|
||||
|
||||
// Usage hint. GL_[DYNAMIC, STATIC, STREAM]_DRAW.
|
||||
BufferUsage usage;
|
||||
|
||||
uint32 mapFlags;
|
||||
|
||||
bool mapped;
|
||||
|
||||
|
||||
@@ -135,6 +135,11 @@ Graphics *Graphics::createInstance(const std::vector<Renderer> &renderers)
|
||||
return instance;
|
||||
}
|
||||
|
||||
Graphics::DisplayState::DisplayState()
|
||||
{
|
||||
defaultSamplerState.mipmapFilter = SamplerState::MIPMAP_FILTER_LINEAR;
|
||||
}
|
||||
|
||||
Graphics::Graphics()
|
||||
: width(0)
|
||||
, height(0)
|
||||
@@ -202,7 +207,7 @@ void Graphics::createQuadIndexBuffer()
|
||||
|
||||
size_t size = sizeof(uint16) * (LOVE_UINT16_MAX / 4) * 6;
|
||||
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_INDEX, 0, BUFFERUSAGE_STATIC);
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_INDEX, BUFFERUSAGE_STATIC);
|
||||
quadIndexBuffer = newBuffer(settings, DATAFORMAT_UINT16, nullptr, size, 0);
|
||||
|
||||
Buffer::Mapper map(*quadIndexBuffer);
|
||||
|
||||
@@ -472,7 +472,7 @@ public:
|
||||
* @param width The viewport width.
|
||||
* @param height The viewport height.
|
||||
**/
|
||||
virtual bool setMode(void *context, int width, int height, int pixelwidth, int pixelheight, bool backbufferstencil, int backbufferdepth) = 0;
|
||||
virtual bool setMode(void *context, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil, int msaa) = 0;
|
||||
|
||||
/**
|
||||
* Un-sets the current graphics display mode (uninitializing objects if
|
||||
@@ -507,6 +507,9 @@ public:
|
||||
double getCurrentDPIScale() const;
|
||||
double getScreenDPIScale() const;
|
||||
|
||||
virtual int getRequestedBackbufferMSAA() const = 0;
|
||||
virtual int getBackbufferMSAA() const = 0;
|
||||
|
||||
/**
|
||||
* Sets the current constant color.
|
||||
**/
|
||||
@@ -874,6 +877,8 @@ protected:
|
||||
|
||||
struct DisplayState
|
||||
{
|
||||
DisplayState();
|
||||
|
||||
Colorf color = Colorf(1.0, 1.0, 1.0, 1.0);
|
||||
Colorf backgroundColor = Colorf(0.0, 0.0, 0.0, 1.0);
|
||||
|
||||
@@ -905,6 +910,7 @@ protected:
|
||||
|
||||
bool wireframe = false;
|
||||
|
||||
// Default mipmap filter is set in the DisplayState constructor.
|
||||
SamplerState defaultSamplerState = SamplerState();
|
||||
};
|
||||
|
||||
|
||||
+84
-154
@@ -46,20 +46,21 @@ std::vector<Buffer::DataDeclaration> Mesh::getDefaultVertexFormat()
|
||||
love::Type Mesh::type("Mesh", &Drawable::type);
|
||||
|
||||
Mesh::Mesh(graphics::Graphics *gfx, const std::vector<Buffer::DataDeclaration> &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, BufferUsage usage)
|
||||
: vertexBuffer(nullptr)
|
||||
, vertexCount(0)
|
||||
, vertexStride(0)
|
||||
, vertexScratchBuffer(nullptr)
|
||||
, indexBuffer(nullptr)
|
||||
, useIndexBuffer(false)
|
||||
, indexCount(0)
|
||||
, indexDataType(INDEX_UINT16)
|
||||
, primitiveType(drawmode)
|
||||
, rangeStart(-1)
|
||||
, rangeCount(-1)
|
||||
: primitiveType(drawmode)
|
||||
{
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, Buffer::MAP_EXPLICIT_RANGE_MODIFY | Buffer::MAP_READ, usage);
|
||||
vertexBuffer.set(gfx->newBuffer(settings, vertexformat, data, datasize, 0), Acquire::NORETAIN);
|
||||
try
|
||||
{
|
||||
vertexData = new uint8[datasize];
|
||||
}
|
||||
catch (std::exception &)
|
||||
{
|
||||
throw love::Exception("Out of memory");
|
||||
}
|
||||
|
||||
memcpy(vertexData, data, datasize);
|
||||
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, usage);
|
||||
vertexBuffer.set(gfx->newBuffer(settings, vertexformat, vertexData, datasize, 0), Acquire::NORETAIN);
|
||||
|
||||
vertexCount = vertexBuffer->getArrayLength();
|
||||
vertexStride = vertexBuffer->getArrayStride();
|
||||
@@ -68,27 +69,17 @@ Mesh::Mesh(graphics::Graphics *gfx, const std::vector<Buffer::DataDeclaration> &
|
||||
setupAttachedAttributes();
|
||||
|
||||
indexDataType = getIndexDataTypeFromMax(vertexCount);
|
||||
|
||||
vertexScratchBuffer = new char[vertexStride];
|
||||
}
|
||||
|
||||
Mesh::Mesh(graphics::Graphics *gfx, const std::vector<Buffer::DataDeclaration> &vertexformat, int vertexcount, PrimitiveType drawmode, BufferUsage usage)
|
||||
: vertexBuffer(nullptr)
|
||||
, vertexCount((size_t) vertexcount)
|
||||
, vertexStride(0)
|
||||
, vertexScratchBuffer(nullptr)
|
||||
, indexBuffer(nullptr)
|
||||
, useIndexBuffer(false)
|
||||
, indexCount(0)
|
||||
: vertexCount((size_t) vertexcount)
|
||||
, indexDataType(getIndexDataTypeFromMax(vertexcount))
|
||||
, primitiveType(drawmode)
|
||||
, rangeStart(-1)
|
||||
, rangeCount(-1)
|
||||
{
|
||||
if (vertexcount <= 0)
|
||||
throw love::Exception("Invalid number of vertices (%d).", vertexcount);
|
||||
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, Buffer::MAP_EXPLICIT_RANGE_MODIFY | Buffer::MAP_READ, usage);
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, usage);
|
||||
vertexBuffer.set(gfx->newBuffer(settings, vertexformat, nullptr, 0, vertexcount), Acquire::NORETAIN);
|
||||
|
||||
vertexStride = vertexBuffer->getArrayStride();
|
||||
@@ -96,25 +87,21 @@ Mesh::Mesh(graphics::Graphics *gfx, const std::vector<Buffer::DataDeclaration> &
|
||||
|
||||
setupAttachedAttributes();
|
||||
|
||||
memset(vertexBuffer->map(), 0, vertexBuffer->getSize());
|
||||
vertexBuffer->setMappedRangeModified(0, vertexBuffer->getSize());
|
||||
vertexBuffer->unmap();
|
||||
try
|
||||
{
|
||||
vertexData = new uint8[vertexBuffer->getSize()];
|
||||
}
|
||||
catch (std::exception &)
|
||||
{
|
||||
throw love::Exception("Out of memory");
|
||||
}
|
||||
|
||||
vertexScratchBuffer = new char[vertexStride];
|
||||
memset(vertexData, 0, vertexBuffer->getSize());
|
||||
vertexBuffer->fill(0, vertexBuffer->getSize(), vertexData);
|
||||
}
|
||||
|
||||
Mesh::Mesh(const std::vector<Mesh::BufferAttribute> &attributes, PrimitiveType drawmode)
|
||||
: vertexBuffer(nullptr)
|
||||
, vertexCount(0)
|
||||
, vertexStride(0)
|
||||
, vertexScratchBuffer(nullptr)
|
||||
, indexBuffer(nullptr)
|
||||
, useIndexBuffer(false)
|
||||
, indexCount(0)
|
||||
, indexDataType(INDEX_UINT16)
|
||||
, primitiveType(drawmode)
|
||||
, rangeStart(-1)
|
||||
, rangeCount(-1)
|
||||
: primitiveType(drawmode)
|
||||
{
|
||||
if (attributes.size() == 0)
|
||||
throw love::Exception("At least one buffer attribute must be specified in this constructor.");
|
||||
@@ -139,7 +126,9 @@ Mesh::Mesh(const std::vector<Mesh::BufferAttribute> &attributes, PrimitiveType d
|
||||
|
||||
Mesh::~Mesh()
|
||||
{
|
||||
delete vertexScratchBuffer;
|
||||
delete vertexData;
|
||||
if (indexData != nullptr)
|
||||
free(indexData);
|
||||
}
|
||||
|
||||
void Mesh::setupAttachedAttributes()
|
||||
@@ -151,7 +140,7 @@ void Mesh::setupAttachedAttributes()
|
||||
if (getAttachedAttributeIndex(name) != -1)
|
||||
throw love::Exception("Duplicate vertex attribute name: %s", name.c_str());
|
||||
|
||||
attachedAttributes.push_back({name, vertexBuffer, (int) i, STEP_PER_VERTEX, true});
|
||||
attachedAttributes.push_back({name, vertexBuffer, nullptr, (int) i, STEP_PER_VERTEX, true});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,89 +155,19 @@ int Mesh::getAttachedAttributeIndex(const std::string &name) const
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Mesh::setVertex(size_t vertindex, const void *data, size_t datasize)
|
||||
void *Mesh::checkVertexDataOffset(size_t vertindex, size_t *byteoffset)
|
||||
{
|
||||
if (vertindex >= vertexCount)
|
||||
throw love::Exception("Invalid vertex index: %ld", vertindex + 1);
|
||||
|
||||
if (vertexBuffer.get() == nullptr)
|
||||
throw love::Exception("setVertex can only be called on a Mesh which owns its own vertex buffer.");
|
||||
if (vertexData == nullptr)
|
||||
throw love::Exception("Mesh must own its own vertex buffer.");
|
||||
|
||||
size_t offset = vertindex * vertexStride;
|
||||
size_t size = std::min(datasize, vertexStride);
|
||||
|
||||
uint8 *bufferdata = (uint8 *) vertexBuffer->map();
|
||||
memcpy(bufferdata + offset, data, size);
|
||||
|
||||
vertexBuffer->setMappedRangeModified(offset, size);
|
||||
}
|
||||
|
||||
size_t Mesh::getVertex(size_t vertindex, void *data, size_t datasize)
|
||||
{
|
||||
if (vertindex >= vertexCount)
|
||||
throw love::Exception("Invalid vertex index: %ld", vertindex + 1);
|
||||
|
||||
if (vertexBuffer.get() == nullptr)
|
||||
throw love::Exception("getVertex can only be called on a Mesh which owns its own vertex buffer.");
|
||||
|
||||
size_t offset = vertindex * vertexStride;
|
||||
size_t size = std::min(datasize, vertexStride);
|
||||
|
||||
// We're relying on map() returning read/write data... ew.
|
||||
const uint8 *bufferdata = (const uint8 *) vertexBuffer->map();
|
||||
memcpy(data, bufferdata + offset, size);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void *Mesh::getVertexScratchBuffer()
|
||||
{
|
||||
return vertexScratchBuffer;
|
||||
}
|
||||
|
||||
void Mesh::setVertexAttribute(size_t vertindex, int attribindex, const void *data, size_t datasize)
|
||||
{
|
||||
if (vertindex >= vertexCount)
|
||||
throw love::Exception("Invalid vertex index: %ld", vertindex + 1);
|
||||
|
||||
if (attribindex >= (int) vertexFormat.size())
|
||||
throw love::Exception("Invalid vertex attribute index: %d", attribindex + 1);
|
||||
|
||||
if (vertexBuffer.get() == nullptr)
|
||||
throw love::Exception("setVertexAttribute can only be called on a Mesh which owns its own vertex buffer.");
|
||||
|
||||
const auto &member = vertexFormat[attribindex];
|
||||
|
||||
size_t offset = vertindex * vertexStride + member.offset;
|
||||
size_t size = std::min(datasize, member.info.size);
|
||||
|
||||
uint8 *bufferdata = (uint8 *) vertexBuffer->map();
|
||||
memcpy(bufferdata + offset, data, size);
|
||||
|
||||
vertexBuffer->setMappedRangeModified(offset, size);
|
||||
}
|
||||
|
||||
size_t Mesh::getVertexAttribute(size_t vertindex, int attribindex, void *data, size_t datasize)
|
||||
{
|
||||
if (vertindex >= vertexCount)
|
||||
throw love::Exception("Invalid vertex index: %ld", vertindex + 1);
|
||||
|
||||
if (attribindex >= (int) vertexFormat.size())
|
||||
throw love::Exception("Invalid vertex attribute index: %d", attribindex + 1);
|
||||
|
||||
if (vertexBuffer.get() == nullptr)
|
||||
throw love::Exception("getVertexAttribute can only be called on a Mesh which owns its own vertex buffer.");
|
||||
|
||||
const auto &member = vertexFormat[attribindex];
|
||||
|
||||
size_t offset = vertindex * vertexStride + member.offset;
|
||||
size_t size = std::min(datasize, member.info.size);
|
||||
|
||||
// We're relying on map() returning read/write data... ew.
|
||||
const uint8 *bufferdata = (const uint8 *) vertexBuffer->map();
|
||||
memcpy(data, bufferdata + offset, size);
|
||||
|
||||
return size;
|
||||
if (byteoffset != nullptr)
|
||||
*byteoffset = offset;
|
||||
return vertexData + offset;
|
||||
}
|
||||
|
||||
size_t Mesh::getVertexCount() const
|
||||
@@ -289,7 +208,7 @@ bool Mesh::isAttributeEnabled(const std::string &name) const
|
||||
return attachedAttributes[index].enabled;
|
||||
}
|
||||
|
||||
void Mesh::attachAttribute(const std::string &name, Buffer *buffer, const std::string &attachname, AttributeStep step)
|
||||
void Mesh::attachAttribute(const std::string &name, Buffer *buffer, Mesh *mesh, const std::string &attachname, AttributeStep step)
|
||||
{
|
||||
if ((buffer->getTypeFlags() & Buffer::TYPEFLAG_VERTEX) == 0)
|
||||
throw love::Exception("Buffer must be created with vertex buffer support to be used as a Mesh vertex attribute.");
|
||||
@@ -309,6 +228,7 @@ void Mesh::attachAttribute(const std::string &name, Buffer *buffer, const std::s
|
||||
|
||||
newattrib.name = name;
|
||||
newattrib.buffer = buffer;
|
||||
newattrib.mesh = mesh;
|
||||
newattrib.enabled = oldattrib.buffer.get() ? oldattrib.enabled : true;
|
||||
newattrib.indexInBuffer = buffer->getDataMemberIndex(attachname);
|
||||
newattrib.step = step;
|
||||
@@ -331,7 +251,7 @@ bool Mesh::detachAttribute(const std::string &name)
|
||||
attachedAttributes.erase(attachedAttributes.begin() + index);
|
||||
|
||||
if (vertexBuffer.get() && vertexBuffer->getDataMemberIndex(name) != -1)
|
||||
attachAttribute(name, vertexBuffer, name);
|
||||
attachAttribute(name, vertexBuffer, nullptr, name);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -341,36 +261,49 @@ const std::vector<Mesh::BufferAttribute> &Mesh::getAttachedAttributes() const
|
||||
return attachedAttributes;
|
||||
}
|
||||
|
||||
void *Mesh::mapVertexData()
|
||||
void *Mesh::getVertexData() const
|
||||
{
|
||||
return vertexBuffer.get() != nullptr ? vertexBuffer->map() : nullptr;
|
||||
return vertexData;
|
||||
}
|
||||
|
||||
void Mesh::unmapVertexData(size_t modifiedoffset, size_t modifiedsize)
|
||||
void Mesh::setVertexDataModified(size_t offset, size_t size)
|
||||
{
|
||||
if (!vertexBuffer.get())
|
||||
return;
|
||||
|
||||
vertexBuffer->setMappedRangeModified(modifiedoffset, modifiedsize);
|
||||
vertexBuffer->unmap();
|
||||
if (vertexData != nullptr)
|
||||
modifiedVertexData.encapsulate(offset, size);
|
||||
}
|
||||
|
||||
void Mesh::flush()
|
||||
{
|
||||
if (vertexBuffer.get())
|
||||
vertexBuffer->unmap();
|
||||
if (vertexBuffer.get() && vertexData != nullptr && modifiedVertexData.isValid())
|
||||
{
|
||||
if (vertexBuffer->getUsage() == BUFFERUSAGE_STREAM)
|
||||
{
|
||||
vertexBuffer->fill(0, vertexBuffer->getSize(), vertexData);
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t offset = modifiedVertexData.getOffset();
|
||||
size_t size = modifiedVertexData.getSize();
|
||||
vertexBuffer->fill(offset, size, vertexData + offset);
|
||||
}
|
||||
|
||||
if (indexBuffer.get())
|
||||
indexBuffer->unmap();
|
||||
modifiedVertexData.invalidate();
|
||||
}
|
||||
|
||||
if (indexDataModified && indexData != nullptr && indexBuffer != nullptr)
|
||||
{
|
||||
indexBuffer->fill(0, indexBuffer->getSize(), indexData);
|
||||
indexDataModified = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies index data from a vector to a mapped index buffer.
|
||||
**/
|
||||
template <typename T>
|
||||
static void copyToIndexBuffer(const std::vector<uint32> &indices, Buffer::Mapper &buffermap, size_t maxval)
|
||||
static void copyToIndexBuffer(const std::vector<uint32> &indices, void *data, size_t maxval)
|
||||
{
|
||||
T *elems = (T *) buffermap.data;
|
||||
T *elems = (T *) data;
|
||||
|
||||
for (size_t i = 0; i < indices.size(); i++)
|
||||
{
|
||||
@@ -395,7 +328,7 @@ void Mesh::setVertexMap(const std::vector<uint32> &map)
|
||||
{
|
||||
auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
|
||||
auto usage = vertexBuffer.get() ? vertexBuffer->getUsage() : BUFFERUSAGE_DYNAMIC;
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_INDEX, Buffer::MAP_READ, usage);
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_INDEX, usage);
|
||||
indexBuffer.set(gfx->newBuffer(settings, dataformat, nullptr, size, 0), Acquire::NORETAIN);
|
||||
}
|
||||
|
||||
@@ -405,17 +338,15 @@ void Mesh::setVertexMap(const std::vector<uint32> &map)
|
||||
if (!indexBuffer || indexCount == 0)
|
||||
return;
|
||||
|
||||
Buffer::Mapper ibomap(*indexBuffer);
|
||||
|
||||
// Fill the buffer with the index values from the vector.
|
||||
switch (datatype)
|
||||
{
|
||||
case INDEX_UINT16:
|
||||
copyToIndexBuffer<uint16>(map, ibomap, maxval);
|
||||
copyToIndexBuffer<uint16>(map, indexData, maxval);
|
||||
break;
|
||||
case INDEX_UINT32:
|
||||
default:
|
||||
copyToIndexBuffer<uint32>(map, ibomap, maxval);
|
||||
copyToIndexBuffer<uint32>(map, indexData, maxval);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -430,7 +361,7 @@ void Mesh::setVertexMap(IndexDataType datatype, const void *data, size_t datasiz
|
||||
{
|
||||
auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
|
||||
auto usage = vertexBuffer.get() ? vertexBuffer->getUsage() : BUFFERUSAGE_DYNAMIC;
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_INDEX, Buffer::MAP_READ, usage);
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_INDEX, usage);
|
||||
indexBuffer.set(gfx->newBuffer(settings, dataformat, nullptr, datasize, 0), Acquire::NORETAIN);
|
||||
}
|
||||
|
||||
@@ -470,24 +401,18 @@ bool Mesh::getVertexMap(std::vector<uint32> &map) const
|
||||
map.clear();
|
||||
map.reserve(indexCount);
|
||||
|
||||
if (!indexBuffer || indexCount == 0)
|
||||
if (indexData == nullptr || indexCount == 0)
|
||||
return true;
|
||||
|
||||
if ((indexBuffer->getMapFlags() & Buffer::MAP_READ) == 0)
|
||||
return false;
|
||||
|
||||
// We unmap the buffer in Mesh::draw, Mesh::setVertexMap, and Mesh::flush.
|
||||
void *buffer = indexBuffer->map();
|
||||
|
||||
// Fill the vector from the buffer.
|
||||
switch (indexDataType)
|
||||
{
|
||||
case INDEX_UINT16:
|
||||
copyFromIndexBuffer<uint16>(buffer, indexCount, map);
|
||||
copyFromIndexBuffer<uint16>(indexData, indexCount, map);
|
||||
break;
|
||||
case INDEX_UINT32:
|
||||
default:
|
||||
copyFromIndexBuffer<uint32>(buffer, indexCount, map);
|
||||
copyFromIndexBuffer<uint32>(indexData, indexCount, map);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -507,6 +432,12 @@ void Mesh::setIndexBuffer(Buffer *buffer)
|
||||
|
||||
if (buffer != nullptr)
|
||||
indexDataType = getIndexDataType(buffer->getDataMember(0).decl.format);
|
||||
|
||||
if (indexData != nullptr)
|
||||
{
|
||||
free(indexData);
|
||||
indexData = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Buffer *Mesh::getIndexBuffer() const
|
||||
@@ -583,6 +514,8 @@ void Mesh::drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount)
|
||||
|
||||
gfx->flushBatchedDraws();
|
||||
|
||||
flush();
|
||||
|
||||
if (Shader::isDefaultActive())
|
||||
Shader::attachDefault(Shader::STANDARD_DEFAULT);
|
||||
|
||||
@@ -612,8 +545,8 @@ void Mesh::drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount)
|
||||
|
||||
if (attributeindex >= 0)
|
||||
{
|
||||
// Make sure the buffer isn't mapped (sends data to GPU if needed.)
|
||||
buffer->unmap();
|
||||
if (attrib.mesh.get())
|
||||
attrib.mesh->flush();
|
||||
|
||||
const auto &member = buffer->getDataMember(attrib.indexInBuffer);
|
||||
|
||||
@@ -637,9 +570,6 @@ void Mesh::drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount)
|
||||
|
||||
if (useIndexBuffer && indexBuffer != nullptr && indexCount > 0)
|
||||
{
|
||||
// Make sure the index buffer isn't mapped (sends data to GPU if needed.)
|
||||
indexBuffer->unmap();
|
||||
|
||||
Graphics::DrawIndexedCommand cmd(&attributes, &buffers, indexBuffer);
|
||||
|
||||
cmd.primitiveType = primitiveType;
|
||||
|
||||
+23
-28
@@ -25,6 +25,7 @@
|
||||
#include "common/int.h"
|
||||
#include "common/math.h"
|
||||
#include "common/StringMap.h"
|
||||
#include "common/Range.h"
|
||||
#include "Drawable.h"
|
||||
#include "Texture.h"
|
||||
#include "vertex.h"
|
||||
@@ -54,6 +55,7 @@ public:
|
||||
{
|
||||
std::string name;
|
||||
StrongRef<Buffer> buffer;
|
||||
StrongRef<Mesh> mesh;
|
||||
int indexInBuffer;
|
||||
AttributeStep step;
|
||||
bool enabled;
|
||||
@@ -68,21 +70,10 @@ public:
|
||||
virtual ~Mesh();
|
||||
|
||||
/**
|
||||
* Sets the values of all attributes at a specific vertex index in the Mesh.
|
||||
* The size of the data must be less than or equal to the total size of all
|
||||
* vertex attributes.
|
||||
* Validates a vertex index and whether the Mesh has its own vertex buffer,
|
||||
* and returns a pointer to the vertex data at the given vertex index.
|
||||
**/
|
||||
void setVertex(size_t vertindex, const void *data, size_t datasize);
|
||||
size_t getVertex(size_t vertindex, void *data, size_t datasize);
|
||||
void *getVertexScratchBuffer();
|
||||
|
||||
/**
|
||||
* Sets the values for a single attribute at a specific vertex index in the
|
||||
* Mesh. The size of the data must be less than or equal to the size of the
|
||||
* attribute.
|
||||
**/
|
||||
void setVertexAttribute(size_t vertindex, int attribindex, const void *data, size_t datasize);
|
||||
size_t getVertexAttribute(size_t vertindex, int attribindex, void *data, size_t datasize);
|
||||
void *checkVertexDataOffset(size_t vertindex, size_t *byteoffset);
|
||||
|
||||
/**
|
||||
* Gets the total number of vertices that can be used when drawing the Mesh.
|
||||
@@ -114,13 +105,16 @@ public:
|
||||
/**
|
||||
* Attaches a vertex attribute from another vertex buffer to this Mesh. The
|
||||
* attribute will be used when drawing this Mesh.
|
||||
* Attributes from other Meshes should also pass in the Mesh as an argument,
|
||||
* to make sure this Mesh knows to flush the passed in Mesh's data to its
|
||||
* buffer when drawing.
|
||||
**/
|
||||
void attachAttribute(const std::string &name, Buffer *buffer, const std::string &attachname, AttributeStep step = STEP_PER_VERTEX);
|
||||
void attachAttribute(const std::string &name, Buffer *buffer, Mesh *mesh, const std::string &attachname, AttributeStep step = STEP_PER_VERTEX);
|
||||
bool detachAttribute(const std::string &name);
|
||||
const std::vector<BufferAttribute> &getAttachedAttributes() const;
|
||||
|
||||
void *mapVertexData();
|
||||
void unmapVertexData(size_t modifiedoffset = 0, size_t modifiedsize = -1);
|
||||
void *getVertexData() const;
|
||||
void setVertexDataModified(size_t offset, size_t size);
|
||||
|
||||
/**
|
||||
* Flushes all modified data to the GPU.
|
||||
@@ -197,23 +191,24 @@ private:
|
||||
|
||||
// Vertex buffer, for the vertex data.
|
||||
StrongRef<Buffer> vertexBuffer;
|
||||
size_t vertexCount;
|
||||
size_t vertexStride;
|
||||
uint8 *vertexData = nullptr;
|
||||
Range modifiedVertexData = Range();
|
||||
|
||||
// Block of memory whose size is at least as large as a single vertex. Helps
|
||||
// avoid memory allocations when using Mesh::setVertex etc.
|
||||
char *vertexScratchBuffer;
|
||||
size_t vertexCount = 0;
|
||||
size_t vertexStride = 0;
|
||||
|
||||
// Index buffer, for the vertex map.
|
||||
StrongRef<Buffer> indexBuffer;
|
||||
bool useIndexBuffer;
|
||||
size_t indexCount;
|
||||
IndexDataType indexDataType;
|
||||
uint8 *indexData = nullptr;
|
||||
bool indexDataModified = false;
|
||||
bool useIndexBuffer = false;
|
||||
size_t indexCount = 0;
|
||||
IndexDataType indexDataType = INDEX_UINT16;
|
||||
|
||||
PrimitiveType primitiveType;
|
||||
PrimitiveType primitiveType = PRIMITIVE_TRIANGLES;
|
||||
|
||||
int rangeStart;
|
||||
int rangeCount;
|
||||
int rangeStart = -1;
|
||||
int rangeCount = -1;
|
||||
|
||||
StrongRef<Texture> texture;
|
||||
|
||||
|
||||
@@ -191,7 +191,7 @@ void ParticleSystem::createBuffers(size_t size)
|
||||
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
|
||||
|
||||
size_t bytes = sizeof(Vertex) * size * 4;
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, 0, BUFFERUSAGE_STREAM);
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, BUFFERUSAGE_STREAM);
|
||||
auto decl = Buffer::getCommonFormatDeclaration(CommonFormat::XYf_STf_RGBAub);
|
||||
buffer = gfx->newBuffer(settings, decl, nullptr, bytes, 0);
|
||||
}
|
||||
@@ -1043,7 +1043,7 @@ void ParticleSystem::draw(Graphics *gfx, const Matrix4 &m)
|
||||
const Vector2 *positions = texture->getQuad()->getVertexPositions();
|
||||
const Vector2 *texcoords = texture->getQuad()->getVertexTexCoords();
|
||||
|
||||
Vertex *pVerts = (Vertex *) buffer->map();
|
||||
Vertex *pVerts = (Vertex *) buffer->map(Buffer::MAP_WRITE_INVALIDATE, 0, buffer->getSize());
|
||||
Particle *p = pHead;
|
||||
|
||||
bool useQuads = !quads.empty();
|
||||
@@ -1079,7 +1079,7 @@ void ParticleSystem::draw(Graphics *gfx, const Matrix4 &m)
|
||||
p = p->next;
|
||||
}
|
||||
|
||||
buffer->unmap();
|
||||
buffer->unmap(0, pCount * sizeof(Vertex) * 4);
|
||||
|
||||
Graphics::TempTransform transform(gfx, m);
|
||||
|
||||
|
||||
@@ -47,6 +47,8 @@ SpriteBatch::SpriteBatch(Graphics *gfx, Texture *texture, int size, BufferUsage
|
||||
, color(255, 255, 255, 255)
|
||||
, colorf(1.0f, 1.0f, 1.0f, 1.0f)
|
||||
, array_buf(nullptr)
|
||||
, vertex_data(nullptr)
|
||||
, modified_sprites()
|
||||
, range_start(-1)
|
||||
, range_count(-1)
|
||||
{
|
||||
@@ -64,14 +66,22 @@ SpriteBatch::SpriteBatch(Graphics *gfx, Texture *texture, int size, BufferUsage
|
||||
vertex_stride = getFormatStride(vertex_format);
|
||||
|
||||
size_t vertex_size = vertex_stride * 4 * size;
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, Buffer::MAP_EXPLICIT_RANGE_MODIFY, usage);
|
||||
|
||||
vertex_data = (uint8 *) malloc(vertex_size);
|
||||
if (vertex_data == nullptr)
|
||||
throw love::Exception("Out of memory.");
|
||||
|
||||
memset(vertex_data, 0, vertex_size);
|
||||
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, usage);
|
||||
auto decl = Buffer::getCommonFormatDeclaration(vertex_format);
|
||||
array_buf = gfx->newBuffer(settings, decl, nullptr, vertex_size, 0);
|
||||
|
||||
array_buf.set(gfx->newBuffer(settings, decl, nullptr, vertex_size, 0), Acquire::NORETAIN);
|
||||
}
|
||||
|
||||
SpriteBatch::~SpriteBatch()
|
||||
{
|
||||
delete array_buf;
|
||||
free(vertex_data);
|
||||
}
|
||||
|
||||
int SpriteBatch::add(const Matrix4 &m, int index /*= -1*/)
|
||||
@@ -93,9 +103,10 @@ int SpriteBatch::add(Quad *quad, const Matrix4 &m, int index /*= -1*/)
|
||||
const Vector2 *quadpositions = quad->getVertexPositions();
|
||||
const Vector2 *quadtexcoords = quad->getVertexTexCoords();
|
||||
|
||||
// Always keep the buffer mapped when adding data (it'll be unmapped on draw.)
|
||||
size_t offset = (index == -1 ? next : index) * vertex_stride * 4;
|
||||
auto verts = (XYf_STf_RGBAub *) ((uint8 *) array_buf->map() + offset);
|
||||
int spriteindex = (index == -1 ? next : index);
|
||||
|
||||
size_t offset = spriteindex * vertex_stride * 4;
|
||||
auto verts = (XYf_STf_RGBAub *) (vertex_data + offset);
|
||||
|
||||
m.transformXY(verts, quadpositions, 4);
|
||||
|
||||
@@ -106,7 +117,7 @@ int SpriteBatch::add(Quad *quad, const Matrix4 &m, int index /*= -1*/)
|
||||
verts[i].color = color;
|
||||
}
|
||||
|
||||
array_buf->setMappedRangeModified(offset, vertex_stride * 4);
|
||||
modified_sprites.encapsulate(spriteindex);
|
||||
|
||||
// Increment counter.
|
||||
if (index == -1)
|
||||
@@ -137,9 +148,10 @@ int SpriteBatch::addLayer(int layer, Quad *quad, const Matrix4 &m, int index)
|
||||
const Vector2 *quadpositions = quad->getVertexPositions();
|
||||
const Vector2 *quadtexcoords = quad->getVertexTexCoords();
|
||||
|
||||
// Always keep the buffer mapped when adding data (it'll be unmapped on draw.)
|
||||
size_t offset = (index == -1 ? next : index) * vertex_stride * 4;
|
||||
auto verts = (XYf_STPf_RGBAub *) ((uint8 *) array_buf->map() + offset);
|
||||
int spriteindex = (index == -1 ? next : index);
|
||||
|
||||
size_t offset = spriteindex * vertex_stride * 4;
|
||||
auto verts = (XYf_STPf_RGBAub *) (vertex_data + offset);
|
||||
|
||||
m.transformXY(verts, quadpositions, 4);
|
||||
|
||||
@@ -151,7 +163,7 @@ int SpriteBatch::addLayer(int layer, Quad *quad, const Matrix4 &m, int index)
|
||||
verts[i].color = color;
|
||||
}
|
||||
|
||||
array_buf->setMappedRangeModified(offset, vertex_stride * 4);
|
||||
modified_sprites.encapsulate(spriteindex);
|
||||
|
||||
// Increment counter.
|
||||
if (index == -1)
|
||||
@@ -168,7 +180,18 @@ void SpriteBatch::clear()
|
||||
|
||||
void SpriteBatch::flush()
|
||||
{
|
||||
array_buf->unmap();
|
||||
if (modified_sprites.isValid())
|
||||
{
|
||||
size_t offset = modified_sprites.getOffset() * vertex_stride * 4;
|
||||
size_t size = modified_sprites.getSize() * vertex_stride * 4;
|
||||
|
||||
if (array_buf->getUsage() == BUFFERUSAGE_STREAM)
|
||||
array_buf->fill(0, array_buf->getSize(), vertex_data);
|
||||
else
|
||||
array_buf->fill(offset, size, vertex_data + offset);
|
||||
|
||||
modified_sprites.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteBatch::setTexture(Texture *newtexture)
|
||||
@@ -213,33 +236,24 @@ void SpriteBatch::setBufferSize(int newsize)
|
||||
return;
|
||||
|
||||
size_t vertex_size = vertex_stride * 4 * newsize;
|
||||
love::graphics::Buffer *new_array_buf = nullptr;
|
||||
|
||||
int new_next = std::min(next, newsize);
|
||||
|
||||
try
|
||||
{
|
||||
auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
|
||||
Buffer::Settings settings(array_buf->getTypeFlags(), array_buf->getMapFlags(), array_buf->getUsage());
|
||||
auto decl = Buffer::getCommonFormatDeclaration(vertex_format);
|
||||
new_array_buf = gfx->newBuffer(settings, decl, nullptr, vertex_size, 0);
|
||||
void *new_vertex_data = realloc(vertex_data, vertex_size);
|
||||
if (new_vertex_data == nullptr)
|
||||
throw love::Exception("Out of memory.");
|
||||
|
||||
// Copy as much of the old data into the new GLBuffer as can fit.
|
||||
size_t copy_size = vertex_stride * 4 * new_next;
|
||||
array_buf->copyTo(0, copy_size, new_array_buf, 0);
|
||||
}
|
||||
catch (love::Exception &)
|
||||
{
|
||||
delete new_array_buf;
|
||||
throw;
|
||||
}
|
||||
auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
|
||||
Buffer::Settings settings(array_buf->getTypeFlags(), array_buf->getUsage());
|
||||
auto decl = Buffer::getCommonFormatDeclaration(vertex_format);
|
||||
|
||||
// We don't need to unmap the old GLBuffer since we're deleting it.
|
||||
delete array_buf;
|
||||
array_buf.set(gfx->newBuffer(settings, decl, nullptr, vertex_size, 0), Acquire::NORETAIN);
|
||||
|
||||
array_buf->fill(0, vertex_stride * 4 * new_next, new_vertex_data);
|
||||
|
||||
vertex_data = (uint8 *) new_vertex_data;
|
||||
|
||||
array_buf = new_array_buf;
|
||||
size = newsize;
|
||||
|
||||
next = new_next;
|
||||
}
|
||||
|
||||
@@ -248,7 +262,7 @@ int SpriteBatch::getBufferSize() const
|
||||
return size;
|
||||
}
|
||||
|
||||
void SpriteBatch::attachAttribute(const std::string &name, Buffer *buffer)
|
||||
void SpriteBatch::attachAttribute(const std::string &name, Buffer *buffer, Mesh *mesh)
|
||||
{
|
||||
if ((buffer->getTypeFlags() & Buffer::TYPEFLAG_VERTEX) == 0)
|
||||
throw love::Exception("GraphicsBuffer must be created with vertex buffer support to be used as a SpriteBatch vertex attribute.");
|
||||
@@ -269,6 +283,7 @@ void SpriteBatch::attachAttribute(const std::string &name, Buffer *buffer)
|
||||
throw love::Exception("The specified Buffer does not have a vertex attribute named '%s'", name.c_str());
|
||||
|
||||
newattrib.buffer = buffer;
|
||||
newattrib.mesh = mesh;
|
||||
|
||||
attached_attributes[name] = newattrib;
|
||||
}
|
||||
@@ -319,8 +334,7 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
|
||||
Shader::current->checkMainTexture(texture);
|
||||
}
|
||||
|
||||
// Make sure the buffer isn't mapped when we draw (sends data to GPU if needed.)
|
||||
array_buf->unmap();
|
||||
flush(); // Upload any modified sprite data to the GPU.
|
||||
|
||||
VertexAttributes attributes;
|
||||
BufferBindings buffers;
|
||||
@@ -353,8 +367,8 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
|
||||
|
||||
if (attributeindex >= 0)
|
||||
{
|
||||
// Make sure the buffer isn't mapped (sends data to GPU if needed.)
|
||||
buffer->unmap();
|
||||
if (it.second.mesh.get())
|
||||
it.second.mesh->flush();
|
||||
|
||||
const auto &member = buffer->getDataMember(it.second.index);
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "common/math.h"
|
||||
#include "common/Matrix.h"
|
||||
#include "common/Color.h"
|
||||
#include "common/Range.h"
|
||||
#include "Drawable.h"
|
||||
#include "Mesh.h"
|
||||
#include "vertex.h"
|
||||
@@ -90,10 +91,13 @@ public:
|
||||
int getBufferSize() const;
|
||||
|
||||
/**
|
||||
* Attaches a specific vertex attribute from a Mesh to this SpriteBatch.
|
||||
* Attaches a specific vertex attribute from a Buffer to this SpriteBatch.
|
||||
* The vertex attribute will be used when drawing the SpriteBatch.
|
||||
* If the attribute comes from a Mesh, it should be given as an argument as
|
||||
* well, to make sure the SpriteBatch flushes its data to its Buffer when
|
||||
* the SpriteBatch is drawn.
|
||||
**/
|
||||
void attachAttribute(const std::string &name, Buffer *buffer);
|
||||
void attachAttribute(const std::string &name, Buffer *buffer, Mesh *mesh);
|
||||
|
||||
void setDrawRange(int start, int count);
|
||||
void setDrawRange();
|
||||
@@ -107,6 +111,7 @@ private:
|
||||
struct AttachedAttribute
|
||||
{
|
||||
StrongRef<Buffer> buffer;
|
||||
StrongRef<Mesh> mesh;
|
||||
int index;
|
||||
};
|
||||
|
||||
@@ -130,8 +135,11 @@ private:
|
||||
|
||||
CommonFormat vertex_format;
|
||||
size_t vertex_stride;
|
||||
|
||||
love::graphics::Buffer *array_buf;
|
||||
|
||||
StrongRef<love::graphics::Buffer> array_buf;
|
||||
uint8 *vertex_data;
|
||||
|
||||
Range modified_sprites;
|
||||
|
||||
std::unordered_map<std::string, AttachedAttribute> attached_attributes;
|
||||
|
||||
|
||||
@@ -33,17 +33,18 @@ love::Type Text::type("Text", &Drawable::type);
|
||||
Text::Text(Font *font, const std::vector<Font::ColoredString> &text)
|
||||
: font(font)
|
||||
, vertexAttributes(Font::vertexFormat, 0)
|
||||
, vertex_buffer(nullptr)
|
||||
, vert_offset(0)
|
||||
, texture_cache_id((uint32) -1)
|
||||
, vertexData(nullptr)
|
||||
, modifiedVertices()
|
||||
, vertOffset(0)
|
||||
, textureCacheID((uint32) -1)
|
||||
{
|
||||
set(text);
|
||||
}
|
||||
|
||||
Text::~Text()
|
||||
{
|
||||
if (vertex_buffer)
|
||||
vertex_buffer->release();
|
||||
if (vertexData != nullptr)
|
||||
free(vertexData);
|
||||
}
|
||||
|
||||
void Text::uploadVertices(const std::vector<Font::GlyphVertex> &vertices, size_t vertoffset)
|
||||
@@ -52,33 +53,40 @@ void Text::uploadVertices(const std::vector<Font::GlyphVertex> &vertices, size_t
|
||||
size_t datasize = vertices.size() * sizeof(Font::GlyphVertex);
|
||||
|
||||
// If we haven't created a VBO or the vertices are too big, make a new one.
|
||||
if (datasize > 0 && (!vertex_buffer || (offset + datasize) > vertex_buffer->getSize()))
|
||||
if (datasize > 0 && (!vertexBuffer || (offset + datasize) > vertexBuffer->getSize()))
|
||||
{
|
||||
// Make it bigger than necessary to reduce potential future allocations.
|
||||
size_t newsize = size_t((offset + datasize) * 1.5);
|
||||
|
||||
if (vertex_buffer != nullptr)
|
||||
newsize = std::max(size_t(vertex_buffer->getSize() * 1.5), newsize);
|
||||
if (vertexBuffer != nullptr)
|
||||
newsize = std::max(size_t(vertexBuffer->getSize() * 1.5), newsize);
|
||||
|
||||
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, 0, BUFFERUSAGE_DYNAMIC);
|
||||
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, BUFFERUSAGE_DYNAMIC);
|
||||
auto decl = Buffer::getCommonFormatDeclaration(Font::vertexFormat);
|
||||
Buffer *new_buffer = gfx->newBuffer(settings, decl, nullptr, newsize, 0);
|
||||
Buffer *newbuffer = gfx->newBuffer(settings, decl, nullptr, newsize, 0);
|
||||
|
||||
if (vertex_buffer != nullptr)
|
||||
vertex_buffer->copyTo(0, vertex_buffer->getSize(), new_buffer, 0);
|
||||
void *newdata = nullptr;
|
||||
if (vertexData != nullptr)
|
||||
newdata = realloc(vertexData, newsize);
|
||||
else
|
||||
newdata = malloc(newsize);
|
||||
|
||||
vertex_buffer->release();
|
||||
vertex_buffer = new_buffer;
|
||||
if (newdata == nullptr)
|
||||
throw love::Exception("Out of memory.");
|
||||
else
|
||||
vertexData = (uint8 *) newdata;
|
||||
|
||||
vertexBuffers.set(0, vertex_buffer, 0);
|
||||
vertexBuffer = newbuffer;
|
||||
|
||||
vertexBuffers.set(0, vertexBuffer, 0);
|
||||
}
|
||||
|
||||
if (vertex_buffer != nullptr && datasize > 0)
|
||||
if (vertexData != nullptr && datasize > 0)
|
||||
{
|
||||
uint8 *bufferdata = (uint8 *) vertex_buffer->map();
|
||||
memcpy(bufferdata + offset, &vertices[0], datasize);
|
||||
// We unmap when we draw, to avoid unnecessary full map()/unmap() calls.
|
||||
memcpy(vertexData + offset, &vertices[0], datasize);
|
||||
modifiedVertices.encapsulate(offset, datasize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,85 +94,85 @@ void Text::regenerateVertices()
|
||||
{
|
||||
// If the font's texture cache was invalidated then we need to recreate the
|
||||
// text's vertices, since glyph texcoords might have changed.
|
||||
if (font->getTextureCacheID() != texture_cache_id)
|
||||
if (font->getTextureCacheID() != textureCacheID)
|
||||
{
|
||||
std::vector<TextData> textdata = text_data;
|
||||
std::vector<TextData> textdata = textData;
|
||||
|
||||
clear();
|
||||
|
||||
for (const TextData &t : textdata)
|
||||
addTextData(t);
|
||||
|
||||
texture_cache_id = font->getTextureCacheID();
|
||||
textureCacheID = font->getTextureCacheID();
|
||||
}
|
||||
}
|
||||
|
||||
void Text::addTextData(const TextData &t)
|
||||
{
|
||||
std::vector<Font::GlyphVertex> vertices;
|
||||
std::vector<Font::DrawCommand> new_commands;
|
||||
std::vector<Font::DrawCommand> newcommands;
|
||||
|
||||
Font::TextInfo text_info;
|
||||
Font::TextInfo textinfo;
|
||||
|
||||
Colorf constantcolor = Colorf(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
// We only have formatted text if the align mode is valid.
|
||||
if (t.align == Font::ALIGN_MAX_ENUM)
|
||||
new_commands = font->generateVertices(t.codepoints, constantcolor, vertices, 0.0f, Vector2(0.0f, 0.0f), &text_info);
|
||||
newcommands = font->generateVertices(t.codepoints, constantcolor, vertices, 0.0f, Vector2(0.0f, 0.0f), &textinfo);
|
||||
else
|
||||
new_commands = font->generateVerticesFormatted(t.codepoints, constantcolor, t.wrap, t.align, vertices, &text_info);
|
||||
newcommands = font->generateVerticesFormatted(t.codepoints, constantcolor, t.wrap, t.align, vertices, &textinfo);
|
||||
|
||||
size_t voffset = vert_offset;
|
||||
size_t voffset = vertOffset;
|
||||
|
||||
// Must be before the early exit below.
|
||||
if (!t.append_vertices)
|
||||
if (!t.appendVertices)
|
||||
{
|
||||
voffset = 0;
|
||||
vert_offset = 0;
|
||||
draw_commands.clear();
|
||||
text_data.clear();
|
||||
vertOffset = 0;
|
||||
drawCommands.clear();
|
||||
textData.clear();
|
||||
}
|
||||
|
||||
if (vertices.empty())
|
||||
return;
|
||||
|
||||
if (t.use_matrix)
|
||||
if (t.useMatrix)
|
||||
t.matrix.transformXY(&vertices[0], &vertices[0], (int) vertices.size());
|
||||
|
||||
uploadVertices(vertices, voffset);
|
||||
|
||||
if (!new_commands.empty())
|
||||
if (!newcommands.empty())
|
||||
{
|
||||
// The start vertex should be adjusted to account for the vertex offset.
|
||||
for (Font::DrawCommand &cmd : new_commands)
|
||||
for (Font::DrawCommand &cmd : newcommands)
|
||||
cmd.startvertex += (int) voffset;
|
||||
|
||||
auto firstcmd = new_commands.begin();
|
||||
auto firstcmd = newcommands.begin();
|
||||
|
||||
// If the first draw command in the new list has the same texture as the
|
||||
// last one in the existing list we're building and its vertices are
|
||||
// in-order, we can combine them (saving a draw call.)
|
||||
if (!draw_commands.empty())
|
||||
if (!drawCommands.empty())
|
||||
{
|
||||
auto prevcmd = draw_commands.back();
|
||||
auto prevcmd = drawCommands.back();
|
||||
if (prevcmd.texture == firstcmd->texture && (prevcmd.startvertex + prevcmd.vertexcount) == firstcmd->startvertex)
|
||||
{
|
||||
draw_commands.back().vertexcount += firstcmd->vertexcount;
|
||||
drawCommands.back().vertexcount += firstcmd->vertexcount;
|
||||
++firstcmd;
|
||||
}
|
||||
}
|
||||
|
||||
// Append the new draw commands to the list we're building.
|
||||
draw_commands.insert(draw_commands.end(), firstcmd, new_commands.end());
|
||||
drawCommands.insert(drawCommands.end(), firstcmd, newcommands.end());
|
||||
}
|
||||
|
||||
vert_offset = voffset + vertices.size();
|
||||
vertOffset = voffset + vertices.size();
|
||||
|
||||
text_data.push_back(t);
|
||||
text_data.back().text_info = text_info;
|
||||
textData.push_back(t);
|
||||
textData.back().textInfo = textinfo;
|
||||
|
||||
// Font::generateVertices can invalidate the font's texture cache.
|
||||
if (font->getTextureCacheID() != texture_cache_id)
|
||||
if (font->getTextureCacheID() != textureCacheID)
|
||||
regenerateVertices();
|
||||
}
|
||||
|
||||
@@ -196,15 +204,15 @@ int Text::addf(const std::vector<Font::ColoredString> &text, float wrap, Font::A
|
||||
|
||||
addTextData({codepoints, wrap, align, {}, true, true, m});
|
||||
|
||||
return (int) text_data.size() - 1;
|
||||
return (int) textData.size() - 1;
|
||||
}
|
||||
|
||||
void Text::clear()
|
||||
{
|
||||
text_data.clear();
|
||||
draw_commands.clear();
|
||||
texture_cache_id = font->getTextureCacheID();
|
||||
vert_offset = 0;
|
||||
textData.clear();
|
||||
drawCommands.clear();
|
||||
textureCacheID = font->getTextureCacheID();
|
||||
vertOffset = 0;
|
||||
}
|
||||
|
||||
void Text::setFont(Font *f)
|
||||
@@ -213,7 +221,7 @@ void Text::setFont(Font *f)
|
||||
|
||||
// Invalidate the texture cache ID since the font is different. We also have
|
||||
// to re-upload all the vertices based on the new font's textures.
|
||||
texture_cache_id = (uint32) -1;
|
||||
textureCacheID = (uint32) -1;
|
||||
regenerateVertices();
|
||||
}
|
||||
|
||||
@@ -225,28 +233,28 @@ Font *Text::getFont() const
|
||||
int Text::getWidth(int index) const
|
||||
{
|
||||
if (index < 0)
|
||||
index = std::max((int) text_data.size() - 1, 0);
|
||||
index = std::max((int) textData.size() - 1, 0);
|
||||
|
||||
if (index >= (int) text_data.size())
|
||||
if (index >= (int) textData.size())
|
||||
return 0;
|
||||
|
||||
return text_data[index].text_info.width;
|
||||
return textData[index].textInfo.width;
|
||||
}
|
||||
|
||||
int Text::getHeight(int index) const
|
||||
{
|
||||
if (index < 0)
|
||||
index = std::max((int) text_data.size() - 1, 0);
|
||||
index = std::max((int) textData.size() - 1, 0);
|
||||
|
||||
if (index >= (int) text_data.size())
|
||||
if (index >= (int) textData.size())
|
||||
return 0;
|
||||
|
||||
return text_data[index].text_info.height;
|
||||
return textData[index].textInfo.height;
|
||||
}
|
||||
|
||||
void Text::draw(Graphics *gfx, const Matrix4 &m)
|
||||
{
|
||||
if (vertex_buffer == nullptr || draw_commands.empty())
|
||||
if (vertexBuffer == nullptr || vertexData == nullptr || drawCommands.empty())
|
||||
return;
|
||||
|
||||
gfx->flushBatchedDraws();
|
||||
@@ -258,18 +266,30 @@ void Text::draw(Graphics *gfx, const Matrix4 &m)
|
||||
Shader::current->checkMainTextureType(TEXTURE_2D, false);
|
||||
|
||||
// Re-generate the text if the Font's texture cache was invalidated.
|
||||
if (font->getTextureCacheID() != texture_cache_id)
|
||||
if (font->getTextureCacheID() != textureCacheID)
|
||||
regenerateVertices();
|
||||
|
||||
int totalverts = 0;
|
||||
for (const Font::DrawCommand &cmd : draw_commands)
|
||||
for (const Font::DrawCommand &cmd : drawCommands)
|
||||
totalverts = std::max(cmd.startvertex + cmd.vertexcount, totalverts);
|
||||
|
||||
vertex_buffer->unmap(); // Make sure all pending data is flushed to the GPU.
|
||||
// Make sure all pending data is uploaded to the GPU.
|
||||
if (modifiedVertices.isValid())
|
||||
{
|
||||
size_t offset = modifiedVertices.getOffset();
|
||||
size_t size = modifiedVertices.getSize();
|
||||
|
||||
if (vertexBuffer->getUsage() == BUFFERUSAGE_STREAM)
|
||||
vertexBuffer->fill(0, vertexBuffer->getSize(), vertexData);
|
||||
else
|
||||
vertexBuffer->fill(offset, size, vertexData + offset);
|
||||
|
||||
modifiedVertices.invalidate();
|
||||
}
|
||||
|
||||
Graphics::TempTransform transform(gfx, m);
|
||||
|
||||
for (const Font::DrawCommand &cmd : draw_commands)
|
||||
for (const Font::DrawCommand &cmd : drawCommands)
|
||||
gfx->drawQuads(cmd.startvertex / 4, cmd.vertexcount / 4, vertexAttributes, vertexBuffers, cmd.texture);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
// LOVE
|
||||
#include "common/config.h"
|
||||
#include "common/Range.h"
|
||||
#include "Drawable.h"
|
||||
#include "Font.h"
|
||||
#include "Buffer.h"
|
||||
@@ -73,9 +74,9 @@ private:
|
||||
Font::ColoredCodepoints codepoints;
|
||||
float wrap;
|
||||
Font::AlignMode align;
|
||||
Font::TextInfo text_info;
|
||||
bool use_matrix;
|
||||
bool append_vertices;
|
||||
Font::TextInfo textInfo;
|
||||
bool useMatrix;
|
||||
bool appendVertices;
|
||||
Matrix4 matrix;
|
||||
};
|
||||
|
||||
@@ -88,16 +89,18 @@ private:
|
||||
VertexAttributes vertexAttributes;
|
||||
BufferBindings vertexBuffers;
|
||||
|
||||
Buffer *vertex_buffer;
|
||||
StrongRef<Buffer> vertexBuffer;
|
||||
uint8 *vertexData;
|
||||
Range modifiedVertices;
|
||||
|
||||
std::vector<Font::DrawCommand> draw_commands;
|
||||
std::vector<Font::DrawCommand> drawCommands;
|
||||
|
||||
std::vector<TextData> text_data;
|
||||
std::vector<TextData> textData;
|
||||
|
||||
size_t vert_offset;
|
||||
size_t vertOffset;
|
||||
|
||||
// Used so we know when the font's texture cache is invalidated.
|
||||
uint32 texture_cache_id;
|
||||
uint32 textureCacheID;
|
||||
|
||||
}; // Text
|
||||
|
||||
|
||||
@@ -173,7 +173,7 @@ Texture::Texture(Graphics *gfx, const Settings &settings, const Slices *slices)
|
||||
, mipmapCount(1)
|
||||
, pixelWidth(0)
|
||||
, pixelHeight(0)
|
||||
, requestedMSAA(settings.msaa)
|
||||
, requestedMSAA(settings.msaa > 1 ? settings.msaa : 0)
|
||||
, samplerState()
|
||||
, graphicsMemorySize(0)
|
||||
, usingDefaultTexture(false)
|
||||
@@ -231,6 +231,9 @@ Texture::Texture(Graphics *gfx, const Settings &settings, const Slices *slices)
|
||||
if (mipmapsMode != MIPMAPS_NONE)
|
||||
mipmapCount = getTotalMipmapCount(pixelWidth, pixelHeight, depth);
|
||||
|
||||
if (mipmapsMode == MIPMAPS_AUTO && isPixelFormatDepthStencil(format))
|
||||
throw love::Exception("Automatic mipmap generation cannot be used for depth/stencil textures.");
|
||||
|
||||
if (pixelWidth <= 0 || pixelHeight <= 0 || layers <= 0 || depth <= 0)
|
||||
throw love::Exception("Texture dimensions must be greater than 0.");
|
||||
|
||||
@@ -279,6 +282,9 @@ Texture::Texture(Graphics *gfx, const Settings &settings, const Slices *slices)
|
||||
|
||||
samplerState = gfx->getDefaultSamplerState();
|
||||
|
||||
if (getMipmapCount() == 1)
|
||||
samplerState.mipmapFilter = SamplerState::MIPMAP_FILTER_NONE;
|
||||
|
||||
Quad::Viewport v = {0, 0, (double) width, (double) height};
|
||||
quad.set(new Quad(v, width, height), Acquire::NORETAIN);
|
||||
|
||||
@@ -485,6 +491,20 @@ void Texture::replacePixels(const void *data, size_t size, int slice, int mipmap
|
||||
generateMipmaps();
|
||||
}
|
||||
|
||||
void Texture::generateMipmaps()
|
||||
{
|
||||
if (getMipmapCount() == 1 || getMipmapsMode() == MIPMAPS_NONE)
|
||||
throw love::Exception("generateMipmaps can only be called on a Texture which was created with mipmaps enabled.");
|
||||
|
||||
if (isPixelFormatCompressed(format))
|
||||
throw love::Exception("generateMipmaps cannot be called on a compressed Texture.");
|
||||
|
||||
if (isPixelFormatDepthStencil(format))
|
||||
throw love::Exception("generateMipmaps cannot be called on a depth/stencil Texture.");
|
||||
|
||||
generateMipmapsInternal();
|
||||
}
|
||||
|
||||
love::image::ImageData *Texture::newImageData(love::image::Image *module, int slice, int mipmap, const Rect &r)
|
||||
{
|
||||
if (!isReadable())
|
||||
@@ -519,7 +539,11 @@ love::image::ImageData *Texture::newImageData(love::image::Image *module, int sl
|
||||
throw love::Exception("ImageData with the '%s' pixel format is not supported.", formatname);
|
||||
}
|
||||
|
||||
return module->newImageData(r.w, r.h, dataformat);
|
||||
auto imagedata = module->newImageData(r.w, r.h, dataformat);
|
||||
|
||||
readbackImageData(imagedata, slice, mipmap, r);
|
||||
|
||||
return imagedata;
|
||||
}
|
||||
|
||||
TextureType Texture::getTextureType() const
|
||||
|
||||
@@ -218,9 +218,9 @@ public:
|
||||
void replacePixels(love::image::ImageDataBase *d, int slice, int mipmap, int x, int y, bool reloadmipmaps);
|
||||
void replacePixels(const void *data, size_t size, int slice, int mipmap, const Rect &rect, bool reloadmipmaps);
|
||||
|
||||
virtual void generateMipmaps() = 0;
|
||||
void generateMipmaps();
|
||||
|
||||
virtual love::image::ImageData *newImageData(love::image::Image *module, int slice, int mipmap, const Rect &rect);
|
||||
love::image::ImageData *newImageData(love::image::Image *module, int slice, int mipmap, const Rect &rect);
|
||||
|
||||
virtual ptrdiff_t getRenderTargetHandle() const = 0;
|
||||
|
||||
@@ -278,6 +278,9 @@ protected:
|
||||
void uploadImageData(love::image::ImageDataBase *d, int level, int slice, int x, int y);
|
||||
virtual void uploadByteData(PixelFormat pixelformat, const void *data, size_t size, int level, int slice, const Rect &r, love::image::ImageDataBase *imgd = nullptr) = 0;
|
||||
|
||||
virtual void generateMipmapsInternal() = 0;
|
||||
virtual void readbackImageData(love::image::ImageData *imagedata, int slice, int mipmap, const Rect &rect) = 0;
|
||||
|
||||
bool validateDimensions(bool throwException) const;
|
||||
|
||||
TextureType texType;
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include "graphics/Buffer.h"
|
||||
#include "Metal.h"
|
||||
#include "common/Range.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
@@ -37,23 +38,21 @@ public:
|
||||
Buffer(love::graphics::Graphics *gfx, id<MTLDevice> device, const Settings &settings, const std::vector<DataDeclaration> &format, const void *data, size_t size, size_t arraylength);
|
||||
virtual ~Buffer();
|
||||
|
||||
void *map() override;
|
||||
void unmap() override;
|
||||
void setMappedRangeModified(size_t offset, size_t size) override;
|
||||
void *map(MapType map, size_t offset, size_t size) override;
|
||||
void unmap(size_t usedoffset, size_t usedsize) override;
|
||||
void fill(size_t offset, size_t size, const void *data) override;
|
||||
|
||||
ptrdiff_t getHandle() const override { return (ptrdiff_t) buffer; }
|
||||
ptrdiff_t getTexelBufferHandle() const override { return (ptrdiff_t) texture; }
|
||||
|
||||
void copyTo(size_t offset, size_t size, love::graphics::Buffer *other, size_t otheroffset) override;
|
||||
|
||||
private:
|
||||
|
||||
id<MTLBuffer> buffer;
|
||||
id<MTLTexture> texture;
|
||||
|
||||
char *memoryMap;
|
||||
id<MTLBuffer> mapBuffer;
|
||||
|
||||
NSRange mappedRange;
|
||||
Range mappedRange;
|
||||
|
||||
}; // Buffer
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
**/
|
||||
|
||||
#import "Buffer.h"
|
||||
#include "Graphics.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
@@ -58,12 +59,13 @@ static MTLPixelFormat getMTLPixelFormat(DataFormat format)
|
||||
Buffer::Buffer(love::graphics::Graphics *gfx, id<MTLDevice> device, const Settings &settings, const std::vector<DataDeclaration> &format, const void *data, size_t size, size_t arraylength)
|
||||
: love::graphics::Buffer(gfx, settings, format, size, arraylength)
|
||||
, texture(nil)
|
||||
, mapBuffer(nil)
|
||||
, mappedRange()
|
||||
{ @autoreleasepool {
|
||||
size = getSize();
|
||||
arraylength = getArrayLength();
|
||||
|
||||
MTLResourceOptions opts = MTLResourceStorageModeManaged;
|
||||
MTLResourceOptions opts = MTLResourceStorageModePrivate;
|
||||
buffer = [device newBufferWithLength:size options:opts];
|
||||
|
||||
if (buffer == nil)
|
||||
@@ -71,25 +73,22 @@ Buffer::Buffer(love::graphics::Graphics *gfx, id<MTLDevice> device, const Settin
|
||||
|
||||
if (typeFlags & TYPEFLAG_TEXEL)
|
||||
{
|
||||
MTLPixelFormat pixformat = getMTLPixelFormat(getDataMember(0).decl.format);
|
||||
auto desc = [MTLTextureDescriptor textureBufferDescriptorWithPixelFormat:pixformat
|
||||
width:size
|
||||
resourceOptions:opts
|
||||
usage:MTLTextureUsageShaderRead];
|
||||
texture = [buffer newTextureWithDescriptor:desc offset:0 bytesPerRow:size];
|
||||
if (@available(iOS 12, macOS 10.14, *))
|
||||
{
|
||||
MTLPixelFormat pixformat = getMTLPixelFormat(getDataMember(0).decl.format);
|
||||
auto desc = [MTLTextureDescriptor textureBufferDescriptorWithPixelFormat:pixformat
|
||||
width:size
|
||||
resourceOptions:opts
|
||||
usage:MTLTextureUsageShaderRead];
|
||||
texture = [buffer newTextureWithDescriptor:desc offset:0 bytesPerRow:size];
|
||||
}
|
||||
|
||||
if (texture == nil)
|
||||
throw love::Exception("Could not create Metal texel buffer.");
|
||||
}
|
||||
|
||||
// TODO: synchronization etc
|
||||
memoryMap = (char *) buffer.contents;
|
||||
|
||||
if (data != nullptr)
|
||||
{
|
||||
memcpy(map(), data, size);
|
||||
unmap();
|
||||
}
|
||||
fill(0, size, data);
|
||||
}}
|
||||
|
||||
Buffer::~Buffer()
|
||||
@@ -98,30 +97,77 @@ Buffer::~Buffer()
|
||||
texture = nil;
|
||||
}}
|
||||
|
||||
void *Buffer::map()
|
||||
{
|
||||
return memoryMap;
|
||||
}
|
||||
|
||||
void Buffer::unmap()
|
||||
void *Buffer::map(MapType /*map*/, size_t offset, size_t size)
|
||||
{ @autoreleasepool {
|
||||
[buffer didModifyRange:{0, size}];
|
||||
if (size == 0)
|
||||
return nullptr;
|
||||
|
||||
Range r(offset, size);
|
||||
|
||||
if (!Range(0, getSize()).contains(r))
|
||||
return nullptr;
|
||||
|
||||
auto gfx = Graphics::getInstance();
|
||||
|
||||
// TODO: Don't create a new buffer every time, also do something for stream
|
||||
// buffers.
|
||||
mapBuffer = [gfx->device newBufferWithLength:size options:MTLResourceStorageModeShared];
|
||||
|
||||
if (mapBuffer != nil)
|
||||
{
|
||||
mappedRange = r;
|
||||
return mapBuffer.contents;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}}
|
||||
|
||||
void Buffer::setMappedRangeModified(size_t offset, size_t size)
|
||||
{
|
||||
mappedRange = NSIntersectionRange(mappedRange, {offset, size});
|
||||
}
|
||||
void Buffer::unmap(size_t usedoffset, size_t usedsize)
|
||||
{ @autoreleasepool {
|
||||
if (mapBuffer == nil)
|
||||
return;
|
||||
|
||||
Range r(usedoffset, usedsize);
|
||||
|
||||
if (!mapped || !mappedRange.contains(r))
|
||||
return;
|
||||
|
||||
auto gfx = Graphics::getInstance();
|
||||
auto encoder = gfx->useBlitEncoder();
|
||||
|
||||
[encoder copyFromBuffer:mapBuffer
|
||||
sourceOffset:(usedoffset - mappedRange.getOffset())
|
||||
toBuffer:buffer
|
||||
destinationOffset:usedoffset
|
||||
size:usedsize];
|
||||
|
||||
mapBuffer = nil;
|
||||
}}
|
||||
|
||||
void Buffer::fill(size_t offset, size_t size, const void *data)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
{ @autoreleasepool {
|
||||
if (size == 0)
|
||||
return;
|
||||
|
||||
void Buffer::copyTo(size_t offset, size_t size, love::graphics::Buffer *other, size_t otheroffset)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
size_t buffersize = getSize();
|
||||
|
||||
if (!Range(0, buffersize).contains(Range(offset, size)))
|
||||
return;
|
||||
|
||||
// TODO: Don't create a new buffer every time, also do something for stream
|
||||
// buffers.
|
||||
auto gfx = Graphics::getInstance();
|
||||
auto encoder = gfx->useBlitEncoder();
|
||||
|
||||
auto tempbuffer = [gfx->device newBufferWithLength:size options:MTLResourceStorageModeShared];
|
||||
memcpy(tempbuffer.contents, data, size);
|
||||
|
||||
[encoder copyFromBuffer:tempbuffer
|
||||
sourceOffset:0
|
||||
toBuffer:buffer
|
||||
destinationOffset:offset
|
||||
size:size];
|
||||
}}
|
||||
|
||||
} // metal
|
||||
} // graphics
|
||||
|
||||
@@ -49,7 +49,7 @@ public:
|
||||
love::graphics::Buffer *newBuffer(const Buffer::Settings &settings, const std::vector<Buffer::DataDeclaration> &format, const void *data, size_t size, size_t arraylength) override;
|
||||
|
||||
void setViewportSize(int width, int height, int pixelwidth, int pixelheight) override;
|
||||
bool setMode(void *context, int width, int height, int pixelwidth, int pixelheight, bool backbufferstencil, int backbufferdepth) override;
|
||||
bool setMode(void *context, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil, int msaa) override;
|
||||
void unSetMode() override;
|
||||
|
||||
void setActive(bool active) override;
|
||||
@@ -65,6 +65,9 @@ public:
|
||||
|
||||
void present(void *screenshotCallbackData) override;
|
||||
|
||||
int getRequestedBackbufferMSAA() const override { return 0; } // TODO
|
||||
int getBackbufferMSAA() const override { return 0; } // TODO
|
||||
|
||||
void setColor(Colorf c) override;
|
||||
|
||||
void setScissor(const Rect &rect) override;
|
||||
|
||||
@@ -188,7 +188,8 @@ Graphics::Graphics()
|
||||
{0.0f, 0.0f, 0.0f, 1.0f},
|
||||
{0, 0, 0, 1},
|
||||
};
|
||||
Buffer::Settings attribsettings(Buffer::TYPEFLAG_VERTEX, 0, BUFFERUSAGE_STATIC);
|
||||
|
||||
Buffer::Settings attribsettings(Buffer::TYPEFLAG_VERTEX, BUFFERUSAGE_STATIC);
|
||||
|
||||
defaultAttributesBuffer = newBuffer(attribsettings, dataformat, &defaults, sizeof(DefaultVertexAttributes), 0);
|
||||
}
|
||||
@@ -288,13 +289,13 @@ void Graphics::setViewportSize(int width, int height, int pixelwidth, int pixelh
|
||||
}
|
||||
}
|
||||
|
||||
bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int pixelheight, bool backbufferstencil, int backbufferdepth)
|
||||
bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil, int msaa)
|
||||
{ @autoreleasepool {
|
||||
this->width = width;
|
||||
this->height = height;
|
||||
this->metalLayer = (__bridge CAMetalLayer *) context;
|
||||
|
||||
this->windowHasStencil = backbufferstencil;
|
||||
this->windowHasStencil = windowhasstencil;
|
||||
|
||||
metalLayer.device = device;
|
||||
metalLayer.pixelFormat = isGammaCorrect() ? MTLPixelFormatBGRA8Unorm_sRGB : MTLPixelFormatBGRA8Unorm;
|
||||
|
||||
@@ -40,8 +40,6 @@ public:
|
||||
Texture(love::graphics::Graphics *gfx, id<MTLDevice> device, const Settings &settings, const Slices *data);
|
||||
virtual ~Texture();
|
||||
|
||||
void generateMipmaps() override;
|
||||
love::image::ImageData *newImageData(love::image::Image *module, int slice, int mipmap, const Rect &rect) override;
|
||||
void setSamplerState(const SamplerState &s) override;
|
||||
|
||||
ptrdiff_t getHandle() const override { return (ptrdiff_t) texture; }
|
||||
@@ -54,6 +52,8 @@ public:
|
||||
private:
|
||||
|
||||
void uploadByteData(PixelFormat pixelformat, const void *data, size_t size, int level, int slice, const Rect &r, love::image::ImageDataBase *imgd = nullptr) override;
|
||||
void generateMipmapsInternal() override;
|
||||
void readbackImageData(love::image::ImageData *imagedata, int slice, int mipmap, const Rect &rect) override;
|
||||
|
||||
id<MTLTexture> texture;
|
||||
id<MTLTexture> msaaTexture;
|
||||
|
||||
@@ -197,7 +197,7 @@ void Texture::uploadByteData(PixelFormat pixelformat, const void *data, size_t s
|
||||
options:options];
|
||||
}}
|
||||
|
||||
void Texture::generateMipmaps()
|
||||
void Texture::generateMipmapsInternal()
|
||||
{ @autoreleasepool {
|
||||
// TODO: alternate method for non-color-renderable and non-filterable
|
||||
// pixel formats.
|
||||
@@ -205,10 +205,9 @@ void Texture::generateMipmaps()
|
||||
[encoder generateMipmapsForTexture:texture];
|
||||
}}
|
||||
|
||||
love::image::ImageData *Texture::newImageData(love::image::Image *module, int slice, int mipmap, const Rect &rect)
|
||||
void Texture::readbackImageData(love::image::ImageData *imagedata, int slice, int mipmap, const Rect &rect)
|
||||
{
|
||||
// TODO
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Texture::setSamplerState(const SamplerState &s)
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include "common/Exception.h"
|
||||
#include "graphics/vertex.h"
|
||||
#include "Graphics.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
@@ -80,22 +81,26 @@ Buffer::Buffer(love::graphics::Graphics *gfx, const Settings &settings, const st
|
||||
|
||||
target = OpenGL::getGLBufferType(mapType);
|
||||
|
||||
try
|
||||
if (usage == BUFFERUSAGE_STREAM)
|
||||
ownsMemoryMap = true;
|
||||
|
||||
std::vector<uint8> emptydata;
|
||||
if (settings.zeroInitialize && data == nullptr)
|
||||
{
|
||||
memoryMap = new char[size];
|
||||
}
|
||||
catch (std::bad_alloc &)
|
||||
{
|
||||
throw love::Exception("Out of memory.");
|
||||
try
|
||||
{
|
||||
emptydata.resize(getSize());
|
||||
data = emptydata.data();
|
||||
}
|
||||
catch (std::exception &)
|
||||
{
|
||||
data = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (data != nullptr)
|
||||
memcpy(memoryMap, data, size);
|
||||
|
||||
if (!load(data != nullptr))
|
||||
if (!load(data))
|
||||
{
|
||||
unloadVolatile();
|
||||
delete[] memoryMap;
|
||||
throw love::Exception("Could not create buffer (out of VRAM?)");
|
||||
}
|
||||
}
|
||||
@@ -103,7 +108,8 @@ Buffer::Buffer(love::graphics::Graphics *gfx, const Settings &settings, const st
|
||||
Buffer::~Buffer()
|
||||
{
|
||||
unloadVolatile();
|
||||
delete[] memoryMap;
|
||||
if (memoryMap != nullptr && ownsMemoryMap)
|
||||
free(memoryMap);
|
||||
}
|
||||
|
||||
bool Buffer::loadVolatile()
|
||||
@@ -111,7 +117,7 @@ bool Buffer::loadVolatile()
|
||||
if (buffer != 0)
|
||||
return true;
|
||||
|
||||
return load(true);
|
||||
return load(nullptr);
|
||||
}
|
||||
|
||||
void Buffer::unloadVolatile()
|
||||
@@ -125,7 +131,7 @@ void Buffer::unloadVolatile()
|
||||
texture = 0;
|
||||
}
|
||||
|
||||
bool Buffer::load(bool restore)
|
||||
bool Buffer::load(const void *initialdata)
|
||||
{
|
||||
while (glGetError() != GL_NO_ERROR)
|
||||
/* Clear the error buffer. */;
|
||||
@@ -133,11 +139,8 @@ bool Buffer::load(bool restore)
|
||||
glGenBuffers(1, &buffer);
|
||||
gl.bindBuffer(mapType, buffer);
|
||||
|
||||
// Copy the old buffer only if 'restore' was requested.
|
||||
const GLvoid *src = restore ? memoryMap : nullptr;
|
||||
|
||||
// Note that if 'src' is '0', no data will be copied.
|
||||
glBufferData(target, (GLsizeiptr) getSize(), src, OpenGL::getGLBufferUsage(getUsage()));
|
||||
// initialdata can be null.
|
||||
glBufferData(target, (GLsizeiptr) getSize(), initialdata, OpenGL::getGLBufferUsage(getUsage()));
|
||||
|
||||
if (getTypeFlags() & TYPEFLAG_TEXEL)
|
||||
{
|
||||
@@ -150,137 +153,104 @@ bool Buffer::load(bool restore)
|
||||
return (glGetError() == GL_NO_ERROR);
|
||||
}
|
||||
|
||||
void *Buffer::map()
|
||||
{
|
||||
if (mapped)
|
||||
return memoryMap;
|
||||
|
||||
mapped = true;
|
||||
|
||||
modifiedOffset = 0;
|
||||
modifiedSize = 0;
|
||||
isMappedDataModified = false;
|
||||
|
||||
return memoryMap;
|
||||
}
|
||||
|
||||
void Buffer::unmapStatic(size_t offset, size_t size)
|
||||
void *Buffer::map(MapType /*map*/, size_t offset, size_t size)
|
||||
{
|
||||
if (size == 0)
|
||||
return;
|
||||
return nullptr;
|
||||
|
||||
// Upload the mapped data to the buffer.
|
||||
gl.bindBuffer(mapType, buffer);
|
||||
glBufferSubData(target, (GLintptr) offset, (GLsizeiptr) size, memoryMap + offset);
|
||||
}
|
||||
Range r(offset, size);
|
||||
|
||||
void Buffer::unmapStream()
|
||||
{
|
||||
GLenum glusage = OpenGL::getGLBufferUsage(getUsage());
|
||||
if (!Range(0, getSize()).contains(r))
|
||||
return nullptr;
|
||||
|
||||
// "orphan" current buffer to avoid implicit synchronisation on the GPU:
|
||||
// http://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-AsynchronousBufferTransfers.pdf
|
||||
gl.bindBuffer(mapType, buffer);
|
||||
glBufferData(target, (GLsizeiptr) getSize(), nullptr, glusage);
|
||||
char *data = nullptr;
|
||||
|
||||
#if LOVE_WINDOWS
|
||||
// TODO: Verify that this codepath is a useful optimization.
|
||||
if (gl.getVendor() == OpenGL::VENDOR_INTEL)
|
||||
glBufferData(target, (GLsizeiptr) getSize(), memoryMap, glusage);
|
||||
if (ownsMemoryMap)
|
||||
{
|
||||
if (memoryMap == nullptr)
|
||||
memoryMap = (char *) malloc(getSize());
|
||||
data = memoryMap;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
glBufferSubData(target, 0, (GLsizeiptr) getSize(), memoryMap);
|
||||
{
|
||||
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
|
||||
data = (char *) gfx->getBufferMapMemory(size);
|
||||
}
|
||||
|
||||
if (data != nullptr)
|
||||
{
|
||||
mapped = true;
|
||||
mappedRange = r;
|
||||
if (!ownsMemoryMap)
|
||||
memoryMap = data;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void Buffer::unmap()
|
||||
void Buffer::unmap(size_t usedoffset, size_t usedsize)
|
||||
{
|
||||
if (!mapped)
|
||||
Range r(usedoffset, usedsize);
|
||||
|
||||
if (!mapped || !mappedRange.contains(r))
|
||||
return;
|
||||
|
||||
mapped = false;
|
||||
|
||||
if ((mapFlags & MAP_EXPLICIT_RANGE_MODIFY) != 0)
|
||||
// Orphan optimization - see fill().
|
||||
if (usage != BUFFERUSAGE_STATIC && mappedRange.first == 0 && mappedRange.getSize() == getSize())
|
||||
{
|
||||
if (!isMappedDataModified)
|
||||
return;
|
||||
|
||||
modifiedOffset = std::min(modifiedOffset, getSize() - 1);
|
||||
modifiedSize = std::min(modifiedSize, getSize() - modifiedOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
modifiedOffset = 0;
|
||||
modifiedSize = getSize();
|
||||
usedoffset = 0;
|
||||
usedsize = getSize();
|
||||
}
|
||||
|
||||
if (modifiedSize > 0)
|
||||
char *data = memoryMap + (usedoffset - mappedRange.getOffset());
|
||||
|
||||
fill(usedoffset, usedsize, data);
|
||||
|
||||
if (!ownsMemoryMap)
|
||||
{
|
||||
switch (getUsage())
|
||||
{
|
||||
case BUFFERUSAGE_STATIC:
|
||||
unmapStatic(modifiedOffset, modifiedSize);
|
||||
break;
|
||||
case BUFFERUSAGE_STREAM:
|
||||
unmapStream();
|
||||
break;
|
||||
case BUFFERUSAGE_DYNAMIC:
|
||||
default:
|
||||
// It's probably more efficient to treat it like a streaming buffer if
|
||||
// at least a third of its contents have been modified during the map().
|
||||
if (modifiedSize >= getSize() / 3)
|
||||
unmapStream();
|
||||
else
|
||||
unmapStatic(modifiedOffset, modifiedSize);
|
||||
break;
|
||||
}
|
||||
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
|
||||
gfx->releaseBufferMapMemory(memoryMap);
|
||||
memoryMap = nullptr;
|
||||
}
|
||||
|
||||
modifiedOffset = 0;
|
||||
modifiedSize = 0;
|
||||
}
|
||||
|
||||
void Buffer::setMappedRangeModified(size_t offset, size_t modifiedsize)
|
||||
{
|
||||
if (!mapped || !(mapFlags & MAP_EXPLICIT_RANGE_MODIFY))
|
||||
return;
|
||||
|
||||
if (!isMappedDataModified)
|
||||
{
|
||||
modifiedOffset = offset;
|
||||
modifiedSize = modifiedsize;
|
||||
isMappedDataModified = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// We're being conservative right now by internally marking the whole range
|
||||
// from the start of section a to the end of section b as modified if both
|
||||
// a and b are marked as modified.
|
||||
|
||||
size_t oldrangeend = modifiedOffset + modifiedSize;
|
||||
modifiedOffset = std::min(modifiedOffset, offset);
|
||||
|
||||
size_t newrangeend = std::max(offset + modifiedsize, oldrangeend);
|
||||
modifiedSize = newrangeend - modifiedOffset;
|
||||
}
|
||||
|
||||
void Buffer::fill(size_t offset, size_t size, const void *data)
|
||||
{
|
||||
memcpy(memoryMap + offset, data, size);
|
||||
if (size == 0)
|
||||
return;
|
||||
|
||||
if (mapped)
|
||||
setMappedRangeModified(offset, size);
|
||||
size_t buffersize = getSize();
|
||||
|
||||
if (!Range(0, buffersize).contains(Range(offset, size)))
|
||||
return;
|
||||
|
||||
GLenum glusage = OpenGL::getGLBufferUsage(usage);
|
||||
|
||||
gl.bindBuffer(mapType, buffer);
|
||||
|
||||
if (usage != BUFFERUSAGE_STATIC && size == buffersize)
|
||||
{
|
||||
// "orphan" current buffer to avoid implicit synchronisation on the GPU:
|
||||
// http://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-AsynchronousBufferTransfers.pdf
|
||||
gl.bindBuffer(mapType, buffer);
|
||||
glBufferData(target, (GLsizeiptr) buffersize, nullptr, glusage);
|
||||
|
||||
#if LOVE_WINDOWS
|
||||
// TODO: Verify that this codepath is a useful optimization.
|
||||
if (gl.getVendor() == OpenGL::VENDOR_INTEL)
|
||||
glBufferData(target, (GLsizeiptr) buffersize, data, glusage);
|
||||
else
|
||||
#endif
|
||||
glBufferSubData(target, 0, (GLsizeiptr) buffersize, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
gl.bindBuffer(mapType, buffer);
|
||||
glBufferSubData(target, (GLintptr) offset, (GLsizeiptr) size, data);
|
||||
}
|
||||
}
|
||||
|
||||
void Buffer::copyTo(size_t offset, size_t size, love::graphics::Buffer *other, size_t otheroffset)
|
||||
{
|
||||
other->fill(otheroffset, size, memoryMap + offset);
|
||||
}
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
// LOVE
|
||||
#include "common/config.h"
|
||||
#include "common/Range.h"
|
||||
#include "graphics/Buffer.h"
|
||||
#include "graphics/Volatile.h"
|
||||
|
||||
@@ -49,19 +50,16 @@ public:
|
||||
bool loadVolatile() override;
|
||||
void unloadVolatile() override;
|
||||
|
||||
void *map() override;
|
||||
void unmap() override;
|
||||
void setMappedRangeModified(size_t offset, size_t size) override;
|
||||
void *map(MapType map, size_t offset, size_t size) override;
|
||||
void unmap(size_t usedoffset, size_t usedsize) override;
|
||||
void fill(size_t offset, size_t size, const void *data) override;
|
||||
|
||||
ptrdiff_t getHandle() const override { return buffer; };
|
||||
ptrdiff_t getTexelBufferHandle() const override { return texture; };
|
||||
|
||||
void copyTo(size_t offset, size_t size, love::graphics::Buffer *other, size_t otheroffset) override;
|
||||
|
||||
private:
|
||||
|
||||
bool load(bool restore);
|
||||
bool load(const void *initialdata);
|
||||
|
||||
void unmapStatic(size_t offset, size_t size);
|
||||
void unmapStream();
|
||||
@@ -77,10 +75,9 @@ private:
|
||||
|
||||
// A pointer to mapped memory.
|
||||
char *memoryMap = nullptr;
|
||||
bool ownsMemoryMap = false;
|
||||
|
||||
size_t modifiedOffset = 0;
|
||||
size_t modifiedSize = 0;
|
||||
bool isMappedDataModified = false;
|
||||
Range mappedRange;
|
||||
|
||||
}; // Buffer
|
||||
|
||||
|
||||
@@ -107,11 +107,24 @@ love::graphics::Graphics *createInstance()
|
||||
Graphics::Graphics()
|
||||
: windowHasStencil(false)
|
||||
, mainVAO(0)
|
||||
, internalBackbufferFBO(0)
|
||||
, requestedBackbufferMSAA(0)
|
||||
, bufferMapMemory(nullptr)
|
||||
, bufferMapMemorySize(2 * 1024 * 1024)
|
||||
, defaultBuffers()
|
||||
, supportedFormats()
|
||||
{
|
||||
gl = OpenGL();
|
||||
|
||||
try
|
||||
{
|
||||
bufferMapMemory = new char[bufferMapMemorySize];
|
||||
}
|
||||
catch (std::exception &)
|
||||
{
|
||||
// Handled in getBufferMapMemory.
|
||||
}
|
||||
|
||||
auto window = getInstance<love::window::Window>(M_WINDOW);
|
||||
|
||||
if (window != nullptr)
|
||||
@@ -121,21 +134,22 @@ Graphics::Graphics()
|
||||
if (window->isOpen())
|
||||
{
|
||||
int w, h;
|
||||
love::window::WindowSettings settings;
|
||||
window->getWindow(w, h, settings);
|
||||
love::window::WindowSettings s;
|
||||
window->getWindow(w, h, s);
|
||||
|
||||
double dpiW = w;
|
||||
double dpiH = h;
|
||||
window->windowToDPICoords(&dpiW, &dpiH);
|
||||
|
||||
void *context = nullptr; // TODO
|
||||
setMode(context, (int) dpiW, (int) dpiH, window->getPixelWidth(), window->getPixelHeight(), settings.stencil, settings.depth);
|
||||
setMode(context, (int) dpiW, (int) dpiH, window->getPixelWidth(), window->getPixelHeight(), s.stencil, s.msaa);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Graphics::~Graphics()
|
||||
{
|
||||
delete[] bufferMapMemory;
|
||||
}
|
||||
|
||||
const char *Graphics::getName() const
|
||||
@@ -188,14 +202,94 @@ void Graphics::setViewportSize(int width, int height, int pixelwidth, int pixelh
|
||||
// Set up the projection matrix
|
||||
projectionMatrix = Matrix4::ortho(0.0, (float) width, (float) height, 0.0, -10.0f, 10.0f);
|
||||
}
|
||||
|
||||
updateBackbuffer(width, height, pixelwidth, pixelheight, requestedBackbufferMSAA);
|
||||
}
|
||||
|
||||
bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int pixelheight, bool backbufferstencil, int /*backbufferdepth*/)
|
||||
void Graphics::updateBackbuffer(int width, int height, int /*pixelwidth*/, int pixelheight, int msaa)
|
||||
{
|
||||
bool useinternalbackbuffer = false;
|
||||
if (msaa > 1)
|
||||
useinternalbackbuffer = true;
|
||||
|
||||
// Our internal backbuffer code needs glBlitFramebuffer.
|
||||
if (!(GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object || GLAD_ES_VERSION_3_0
|
||||
|| GLAD_EXT_framebuffer_blit || GLAD_ANGLE_framebuffer_blit || GLAD_NV_framebuffer_blit))
|
||||
{
|
||||
if (!(msaa > 1 && GLAD_APPLE_framebuffer_multisample))
|
||||
useinternalbackbuffer = false;
|
||||
}
|
||||
|
||||
GLuint prevFBO = gl.getFramebuffer(OpenGL::FRAMEBUFFER_ALL);
|
||||
bool restoreFBO = prevFBO != getInternalBackbufferFBO();
|
||||
|
||||
if (useinternalbackbuffer)
|
||||
{
|
||||
Texture::Settings settings;
|
||||
settings.width = width;
|
||||
settings.height = height;
|
||||
settings.dpiScale = (float)pixelheight / (float)height;
|
||||
settings.msaa = msaa;
|
||||
settings.renderTarget = true;
|
||||
settings.readable.set(false);
|
||||
|
||||
settings.format = isGammaCorrect() ? PIXELFORMAT_RGBA8_UNORM_sRGB : PIXELFORMAT_RGBA8_UNORM;
|
||||
internalBackbuffer.set(newTexture(settings), Acquire::NORETAIN);
|
||||
|
||||
settings.format = PIXELFORMAT_DEPTH24_UNORM_STENCIL8;
|
||||
internalBackbufferDepthStencil.set(newTexture(settings), Acquire::NORETAIN);
|
||||
|
||||
RenderTargets rts;
|
||||
rts.colors.push_back(internalBackbuffer.get());
|
||||
rts.depthStencil.texture = internalBackbufferDepthStencil;
|
||||
|
||||
internalBackbufferFBO = bindCachedFBO(rts);
|
||||
}
|
||||
else
|
||||
{
|
||||
internalBackbuffer.set(nullptr);
|
||||
internalBackbufferDepthStencil.set(nullptr);
|
||||
internalBackbufferFBO = 0;
|
||||
}
|
||||
|
||||
requestedBackbufferMSAA = msaa;
|
||||
|
||||
if (restoreFBO)
|
||||
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, prevFBO);
|
||||
}
|
||||
|
||||
GLuint Graphics::getInternalBackbufferFBO() const
|
||||
{
|
||||
if (internalBackbufferFBO != 0)
|
||||
return internalBackbufferFBO;
|
||||
else
|
||||
return getSystemBackbufferFBO();
|
||||
}
|
||||
|
||||
GLuint Graphics::getSystemBackbufferFBO() const
|
||||
{
|
||||
#ifdef LOVE_IOS
|
||||
// Hack: iOS uses a custom FBO.
|
||||
SDL_SysWMinfo info = {};
|
||||
SDL_VERSION(&info.version);
|
||||
SDL_GetWindowWMInfo(SDL_GL_GetCurrentWindow(), &info);
|
||||
|
||||
if (info.info.uikit.resolveFramebuffer != 0)
|
||||
return info.info.uikit.resolveFramebuffer;
|
||||
else
|
||||
return info.info.uikit.framebuffer;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Graphics::setMode(void */*context*/, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil, int msaa)
|
||||
{
|
||||
this->width = width;
|
||||
this->height = height;
|
||||
|
||||
this->windowHasStencil = backbufferstencil;
|
||||
this->windowHasStencil = windowhasstencil;
|
||||
this->requestedBackbufferMSAA = msaa;
|
||||
|
||||
// Okay, setup OpenGL.
|
||||
gl.initContext();
|
||||
@@ -211,8 +305,6 @@ bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int
|
||||
created = true;
|
||||
initCapabilities();
|
||||
|
||||
setViewportSize(width, height, pixelwidth, pixelheight);
|
||||
|
||||
// Enable blending
|
||||
gl.setEnableState(OpenGL::ENABLE_BLEND, true);
|
||||
|
||||
@@ -252,6 +344,8 @@ bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int
|
||||
|
||||
setDebug(isDebugEnabled());
|
||||
|
||||
setViewportSize(width, height, pixelwidth, pixelheight);
|
||||
|
||||
if (batchedDrawState.vb[0] == nullptr)
|
||||
{
|
||||
// Initial sizes that should be good enough for most cases. It will
|
||||
@@ -263,7 +357,7 @@ bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int
|
||||
|
||||
if (capabilities.features[FEATURE_TEXEL_BUFFER] && defaultBuffers[BUFFERTYPE_TEXEL].get() == nullptr)
|
||||
{
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_TEXEL, 0, BUFFERUSAGE_STATIC);
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_TEXEL, BUFFERUSAGE_STATIC);
|
||||
std::vector<Buffer::DataDeclaration> format = {{"", DATAFORMAT_FLOAT_VEC4, 0}};
|
||||
|
||||
const float texel[] = {0.0f, 0.0f, 0.0f, 1.0f};
|
||||
@@ -335,6 +429,9 @@ void Graphics::unSetMode()
|
||||
|
||||
flushBatchedDraws();
|
||||
|
||||
internalBackbuffer.set(nullptr);
|
||||
internalBackbufferDepthStencil.set(nullptr);
|
||||
|
||||
// Unload all volatile objects. These must be reloaded after the display
|
||||
// mode change.
|
||||
Volatile::unloadAll();
|
||||
@@ -552,7 +649,7 @@ void Graphics::setRenderTargetsInternal(const RenderTargets &rts, int w, int h,
|
||||
|
||||
if (iswindow)
|
||||
{
|
||||
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, gl.getDefaultFBO());
|
||||
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, getInternalBackbufferFBO());
|
||||
|
||||
// The projection matrix is flipped compared to rendering to a texture,
|
||||
// due to OpenGL considering (0,0) bottom-left instead of top-left.
|
||||
@@ -594,6 +691,8 @@ void Graphics::endPass()
|
||||
// Discard the depth/stencil buffer if we're using an internal cached one.
|
||||
if (depthstencil == nullptr && (rts.temporaryRTFlags & (TEMPORARY_RT_DEPTH | TEMPORARY_RT_STENCIL)) != 0)
|
||||
discard({}, true);
|
||||
else if (!rts.getFirstTarget().texture.get())
|
||||
discard({}, true); // Backbuffer
|
||||
|
||||
// Resolve MSAA buffers. MSAA is only supported for 2D render targets so we
|
||||
// don't have to worry about resolving to slices.
|
||||
@@ -646,15 +745,12 @@ void Graphics::endPass()
|
||||
}
|
||||
}
|
||||
|
||||
// generateMipmaps can't be used for depth/stencil textures.
|
||||
for (const auto &rt : rts.colors)
|
||||
{
|
||||
if (rt.texture->getMipmapsMode() == Texture::MIPMAPS_AUTO && rt.mipmap == 0)
|
||||
rt.texture->generateMipmaps();
|
||||
}
|
||||
|
||||
int dsmipmap = rts.depthStencil.mipmap;
|
||||
if (depthstencil != nullptr && depthstencil->getMipmapsMode() == Texture::MIPMAPS_AUTO && dsmipmap == 0)
|
||||
depthstencil->generateMipmaps();
|
||||
}
|
||||
|
||||
void Graphics::clear(OptionalColorf c, OptionalInt stencil, OptionalDouble depth)
|
||||
@@ -812,7 +908,7 @@ void Graphics::discard(OpenGL::FramebufferTarget target, const std::vector<bool>
|
||||
attachments.reserve(colorbuffers.size());
|
||||
|
||||
// glDiscardFramebuffer uses different attachment enums for the default FBO.
|
||||
if (!isRenderTargetActive() && gl.getDefaultFBO() == 0)
|
||||
if (!isRenderTargetActive() && getInternalBackbufferFBO() == 0)
|
||||
{
|
||||
if (colorbuffers.size() > 0 && colorbuffers[0])
|
||||
attachments.push_back(GL_COLOR);
|
||||
@@ -879,7 +975,7 @@ void Graphics::cleanupRenderTexture(love::graphics::Texture *texture)
|
||||
}
|
||||
}
|
||||
|
||||
void Graphics::bindCachedFBO(const RenderTargets &targets)
|
||||
GLuint Graphics::bindCachedFBO(const RenderTargets &targets)
|
||||
{
|
||||
GLuint fbo = framebufferObjects[targets];
|
||||
|
||||
@@ -962,6 +1058,8 @@ void Graphics::bindCachedFBO(const RenderTargets &targets)
|
||||
|
||||
framebufferObjects[targets] = fbo;
|
||||
}
|
||||
|
||||
return fbo;
|
||||
}
|
||||
|
||||
void Graphics::present(void *screenshotCallbackData)
|
||||
@@ -977,13 +1075,34 @@ void Graphics::present(void *screenshotCallbackData)
|
||||
flushBatchedDraws();
|
||||
endPass();
|
||||
|
||||
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, gl.getDefaultFBO());
|
||||
int w = getPixelWidth();
|
||||
int h = getPixelHeight();
|
||||
|
||||
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, getInternalBackbufferFBO());
|
||||
|
||||
// Copy internal backbuffer to system backbuffer. When MSAA is used this
|
||||
// is a direct MSAA resolve.
|
||||
if (internalBackbuffer.get())
|
||||
{
|
||||
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_DRAW, getSystemBackbufferFBO());
|
||||
|
||||
// Discard system backbuffer to prevent it from copying its contents
|
||||
// from VRAM to chip memory.
|
||||
discard(OpenGL::FRAMEBUFFER_DRAW, {true}, true);
|
||||
|
||||
// updateBackbuffer checks for glBlitFramebuffer support.
|
||||
if (GLAD_APPLE_framebuffer_multisample && internalBackbuffer->getMSAA() > 1)
|
||||
glResolveMultisampleFramebufferAPPLE();
|
||||
else
|
||||
glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
|
||||
// Discarding the internal backbuffer directly after resolving it should
|
||||
// eliminate any copy back to vram it might need to do.
|
||||
discard(OpenGL::FRAMEBUFFER_READ, {true}, false);
|
||||
}
|
||||
|
||||
if (!pendingScreenshotCallbacks.empty())
|
||||
{
|
||||
int w = getPixelWidth();
|
||||
int h = getPixelHeight();
|
||||
|
||||
size_t row = 4 * w;
|
||||
size_t size = row * h;
|
||||
|
||||
@@ -1002,26 +1121,7 @@ void Graphics::present(void *screenshotCallbackData)
|
||||
throw love::Exception("Out of memory.");
|
||||
}
|
||||
|
||||
#ifdef LOVE_IOS
|
||||
SDL_SysWMinfo info = {};
|
||||
SDL_VERSION(&info.version);
|
||||
SDL_GetWindowWMInfo(SDL_GL_GetCurrentWindow(), &info);
|
||||
|
||||
if (info.info.uikit.resolveFramebuffer != 0)
|
||||
{
|
||||
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_DRAW, info.info.uikit.resolveFramebuffer);
|
||||
|
||||
// We need to do an explicit MSAA resolve on iOS, because it uses
|
||||
// GLES FBOs rather than a system framebuffer.
|
||||
if (GLAD_ES_VERSION_3_0)
|
||||
glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
else if (GLAD_APPLE_framebuffer_multisample)
|
||||
glResolveMultisampleFramebufferAPPLE();
|
||||
|
||||
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_READ, info.info.uikit.resolveFramebuffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, getSystemBackbufferFBO());
|
||||
glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
||||
|
||||
// Replace alpha values with full opacity.
|
||||
@@ -1085,6 +1185,8 @@ void Graphics::present(void *screenshotCallbackData)
|
||||
if (window != nullptr)
|
||||
window->swapBuffers();
|
||||
|
||||
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, getInternalBackbufferFBO());
|
||||
|
||||
// Reset the per-frame stat counts.
|
||||
drawCalls = 0;
|
||||
gl.stats.shaderSwitches = 0;
|
||||
@@ -1105,6 +1207,16 @@ void Graphics::present(void *screenshotCallbackData)
|
||||
}
|
||||
}
|
||||
|
||||
int Graphics::getRequestedBackbufferMSAA() const
|
||||
{
|
||||
return requestedBackbufferMSAA;
|
||||
}
|
||||
|
||||
int Graphics::getBackbufferMSAA() const
|
||||
{
|
||||
return internalBackbuffer.get() ? internalBackbuffer->getMSAA() : 0;
|
||||
}
|
||||
|
||||
void Graphics::setScissor(const Rect &rect)
|
||||
{
|
||||
flushBatchedDraws();
|
||||
@@ -1354,6 +1466,21 @@ void Graphics::setWireframe(bool enable)
|
||||
states.back().wireframe = enable;
|
||||
}
|
||||
|
||||
void *Graphics::getBufferMapMemory(size_t size)
|
||||
{
|
||||
// We don't need anything more complicated because get/release calls are
|
||||
// never interleaved (as of when this comment was written.)
|
||||
if (bufferMapMemory == nullptr || size > bufferMapMemorySize)
|
||||
return malloc(size);
|
||||
return bufferMapMemory;
|
||||
}
|
||||
|
||||
void Graphics::releaseBufferMapMemory(void *mem)
|
||||
{
|
||||
if (mem != bufferMapMemory)
|
||||
free(mem);
|
||||
}
|
||||
|
||||
Graphics::Renderer Graphics::getRenderer() const
|
||||
{
|
||||
return RENDERER_OPENGL;
|
||||
|
||||
@@ -63,7 +63,7 @@ public:
|
||||
love::graphics::Buffer *newBuffer(const Buffer::Settings &settings, const std::vector<Buffer::DataDeclaration> &format, const void *data, size_t size, size_t arraylength) override;
|
||||
|
||||
void setViewportSize(int width, int height, int pixelwidth, int pixelheight) override;
|
||||
bool setMode(void *context, int width, int height, int pixelwidth, int pixelheight, bool backbufferstencil, int backbufferdepth) override;
|
||||
bool setMode(void *context, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil, int msaa) override;
|
||||
void unSetMode() override;
|
||||
|
||||
void setActive(bool active) override;
|
||||
@@ -79,6 +79,9 @@ public:
|
||||
|
||||
void present(void *screenshotCallbackData) override;
|
||||
|
||||
int getRequestedBackbufferMSAA() const override;
|
||||
int getBackbufferMSAA() const override;
|
||||
|
||||
void setColor(Colorf c) override;
|
||||
|
||||
void setScissor(const Rect &rect) override;
|
||||
@@ -110,6 +113,9 @@ public:
|
||||
// Internal use.
|
||||
void cleanupRenderTexture(love::graphics::Texture *texture);
|
||||
|
||||
void *getBufferMapMemory(size_t size);
|
||||
void releaseBufferMapMemory(void *mem);
|
||||
|
||||
private:
|
||||
|
||||
struct CachedFBOHasher
|
||||
@@ -139,15 +145,27 @@ private:
|
||||
void getAPIStats(int &shaderswitches) const override;
|
||||
|
||||
void endPass();
|
||||
void bindCachedFBO(const RenderTargets &targets);
|
||||
GLuint bindCachedFBO(const RenderTargets &targets);
|
||||
void discard(OpenGL::FramebufferTarget target, const std::vector<bool> &colorbuffers, bool depthstencil);
|
||||
|
||||
void updateBackbuffer(int width, int height, int pixelwidth, int pixelheight, int msaa);
|
||||
GLuint getInternalBackbufferFBO() const;
|
||||
GLuint getSystemBackbufferFBO() const;
|
||||
|
||||
void setDebug(bool enable);
|
||||
|
||||
std::unordered_map<RenderTargets, GLuint, CachedFBOHasher> framebufferObjects;
|
||||
bool windowHasStencil;
|
||||
GLuint mainVAO;
|
||||
|
||||
StrongRef<love::graphics::Texture> internalBackbuffer;
|
||||
StrongRef<love::graphics::Texture> internalBackbufferDepthStencil;
|
||||
GLuint internalBackbufferFBO;
|
||||
int requestedBackbufferMSAA;
|
||||
|
||||
char *bufferMapMemory;
|
||||
size_t bufferMapMemorySize;
|
||||
|
||||
// Only needed for buffer types that can be bound to shaders.
|
||||
StrongRef<love::graphics::Buffer> defaultBuffers[BUFFERTYPE_MAX_ENUM];
|
||||
|
||||
|
||||
@@ -752,30 +752,8 @@ void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffe
|
||||
buffer->retain();
|
||||
}
|
||||
|
||||
bool addbuffertoarray = true;
|
||||
|
||||
if (info->buffers[i] != nullptr)
|
||||
{
|
||||
Buffer *oldbuffer = info->buffers[i];
|
||||
auto it = std::find(buffersToUnmap.begin(), buffersToUnmap.end(), oldbuffer);
|
||||
if (it != buffersToUnmap.end())
|
||||
{
|
||||
addbuffertoarray = false;
|
||||
if (buffer != nullptr)
|
||||
*it = buffer;
|
||||
else
|
||||
{
|
||||
auto last = buffersToUnmap.end() - 1;
|
||||
*it = *last;
|
||||
buffersToUnmap.erase(last);
|
||||
}
|
||||
}
|
||||
|
||||
oldbuffer->release();
|
||||
}
|
||||
|
||||
if (addbuffertoarray && buffer != nullptr)
|
||||
buffersToUnmap.push_back(buffer);
|
||||
info->buffers[i]->release();
|
||||
|
||||
info->buffers[i] = buffer;
|
||||
|
||||
@@ -907,13 +885,6 @@ void Shader::updateBuiltinUniforms(love::graphics::Graphics *gfx, int viewportW,
|
||||
GLint location = builtinUniforms[BUILTIN_UNIFORMS_PER_DRAW];
|
||||
if (location >= 0)
|
||||
glUniform4fv(location, 13, (const GLfloat *) &data);
|
||||
|
||||
// TODO: Find a better place to put this.
|
||||
// Buffers used in this shader can be mapped by external code without
|
||||
// unmapping. We need to make sure the data on the GPU is up to date,
|
||||
// otherwise the shader can read from old data.
|
||||
for (Buffer *buffer : buffersToUnmap)
|
||||
buffer->unmap();
|
||||
}
|
||||
|
||||
int Shader::getUniformTypeComponents(GLenum type) const
|
||||
|
||||
@@ -116,8 +116,6 @@ private:
|
||||
|
||||
std::vector<std::pair<const UniformInfo *, int>> pendingUniformUpdates;
|
||||
|
||||
std::vector<Buffer *> buffersToUnmap;
|
||||
|
||||
float lastPointSize;
|
||||
|
||||
}; // Shader
|
||||
|
||||
@@ -499,7 +499,7 @@ love::graphics::StreamBuffer *CreateStreamBuffer(BufferType mode, size_t size)
|
||||
{
|
||||
// AMD's pinned memory seems to be faster than persistent mapping,
|
||||
// on AMD GPUs.
|
||||
if (GLAD_AMD_pinned_memory)
|
||||
if (GLAD_AMD_pinned_memory && gl.getVendor() == OpenGL::VENDOR_AMD)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
@@ -218,6 +218,10 @@ Texture::Texture(love::graphics::Graphics *gfx, const Settings &settings, const
|
||||
if (textureGLError != GL_NO_ERROR)
|
||||
throw love::Exception("Cannot create Texture (OpenGL error: %s)", OpenGL::errorString(textureGLError));
|
||||
}
|
||||
|
||||
// ImageData is referenced by the first loadVolatile call, but we don't
|
||||
// hang on to it after that so we can save memory.
|
||||
slices.clear();
|
||||
}
|
||||
|
||||
Texture::~Texture()
|
||||
@@ -480,14 +484,8 @@ void Texture::uploadByteData(PixelFormat pixelformat, const void *data, size_t s
|
||||
}
|
||||
}
|
||||
|
||||
void Texture::generateMipmaps()
|
||||
void Texture::generateMipmapsInternal()
|
||||
{
|
||||
if (getMipmapCount() == 1 || getMipmapsMode() == MIPMAPS_NONE)
|
||||
throw love::Exception("generateMipmaps can only be called on a Texture which was created with mipmaps enabled.");
|
||||
|
||||
if (isPixelFormatCompressed(format))
|
||||
throw love::Exception("generateMipmaps cannot be called on a compressed Texture.");
|
||||
|
||||
gl.bindTextureToUnit(this, 0, false);
|
||||
|
||||
GLenum gltextype = OpenGL::getGLTextureType(texType);
|
||||
@@ -498,13 +496,10 @@ void Texture::generateMipmaps()
|
||||
glGenerateMipmap(gltextype);
|
||||
}
|
||||
|
||||
love::image::ImageData *Texture::newImageData(love::image::Image *module, int slice, int mipmap, const Rect &r)
|
||||
void Texture::readbackImageData(love::image::ImageData *data, int slice, int mipmap, const Rect &r)
|
||||
{
|
||||
// Base class does validation (only RTs allowed, etc) and creates ImageData.
|
||||
love::image::ImageData *data = love::graphics::Texture::newImageData(module, slice, mipmap, r);
|
||||
|
||||
if (fbo == 0) // Should never be reached.
|
||||
return data;
|
||||
return;
|
||||
|
||||
bool isSRGB = false;
|
||||
OpenGL::TextureFormat fmt = gl.convertPixelFormat(data->getFormat(), false, isSRGB);
|
||||
@@ -525,8 +520,6 @@ love::image::ImageData *Texture::newImageData(love::image::Image *module, int sl
|
||||
gl.framebufferTexture(GL_COLOR_ATTACHMENT0, texType, texture, 0, 0, 0);
|
||||
|
||||
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, current_fbo);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void Texture::setSamplerState(const SamplerState &s)
|
||||
|
||||
@@ -46,8 +46,6 @@ public:
|
||||
bool loadVolatile() override;
|
||||
void unloadVolatile() override;
|
||||
|
||||
void generateMipmaps() override;
|
||||
love::image::ImageData *newImageData(love::image::Image *module, int slice, int mipmap, const Rect &rect) override;
|
||||
void setSamplerState(const SamplerState &s) override;
|
||||
|
||||
ptrdiff_t getHandle() const override;
|
||||
@@ -62,6 +60,10 @@ private:
|
||||
|
||||
void uploadByteData(PixelFormat pixelformat, const void *data, size_t size, int level, int slice, const Rect &r, love::image::ImageDataBase *imgd = nullptr) override;
|
||||
|
||||
void generateMipmapsInternal() override;
|
||||
|
||||
void readbackImageData(love::image::ImageData *imagedata, int slice, int mipmap, const Rect &rect) override;
|
||||
|
||||
Slices slices;
|
||||
|
||||
GLuint fbo;
|
||||
|
||||
@@ -186,13 +186,6 @@ Buffer *luax_checkbuffer(lua_State *L, int idx)
|
||||
return luax_checktype<Buffer>(L, idx);
|
||||
}
|
||||
|
||||
static int w_Buffer_flush(lua_State *L)
|
||||
{
|
||||
Buffer *t = luax_checkbuffer(L, 1);
|
||||
t->unmap();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int w_Buffer_setArrayData(lua_State *L)
|
||||
{
|
||||
Buffer *t = luax_checkbuffer(L, 1);
|
||||
@@ -223,12 +216,8 @@ static int w_Buffer_setArrayData(lua_State *L)
|
||||
return luaL_error(L, "Too many array elements (expected at most %d, got %d)", arraylength - startindex, count);
|
||||
|
||||
size_t datasize = std::min(d->getSize(), count * stride);
|
||||
char *bytedata = (char *) t->map() + offset;
|
||||
|
||||
memcpy(bytedata, d->getData(), datasize);
|
||||
|
||||
t->setMappedRangeModified(offset, datasize);
|
||||
t->unmap();
|
||||
t->fill(offset, datasize, d->getData());
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -256,7 +245,7 @@ static int w_Buffer_setArrayData(lua_State *L)
|
||||
if (startindex + count > arraylength)
|
||||
return luaL_error(L, "Too many array elements (expected at most %d, got %d)", arraylength - startindex, count);
|
||||
|
||||
char *data = (char *) t->map() + offset;
|
||||
char *data = (char *) t->map(Buffer::MAP_WRITE_INVALIDATE, offset, count * stride);
|
||||
|
||||
if (tableoftables)
|
||||
{
|
||||
@@ -303,8 +292,7 @@ static int w_Buffer_setArrayData(lua_State *L)
|
||||
}
|
||||
}
|
||||
|
||||
t->setMappedRangeModified(offset, count * stride);
|
||||
t->unmap();
|
||||
t->unmap(offset, count * stride);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -376,7 +364,6 @@ static int w_Buffer_isBufferType(lua_State *L)
|
||||
|
||||
static const luaL_Reg w_Buffer_functions[] =
|
||||
{
|
||||
{ "flush", w_Buffer_flush },
|
||||
{ "setArrayData", w_Buffer_setArrayData },
|
||||
{ "getElementCount", w_Buffer_getElementCount },
|
||||
{ "getElementStride", w_Buffer_getElementStride },
|
||||
|
||||
@@ -1559,7 +1559,7 @@ static void luax_checkbufferformat(lua_State *L, int idx, std::vector<Buffer::Da
|
||||
}
|
||||
}
|
||||
|
||||
static Buffer *luax_newbuffer(lua_State *L, int idx, const Buffer::Settings &settings, const std::vector<Buffer::DataDeclaration> &format)
|
||||
static Buffer *luax_newbuffer(lua_State *L, int idx, Buffer::Settings settings, const std::vector<Buffer::DataDeclaration> &format)
|
||||
{
|
||||
size_t arraylength = 0;
|
||||
size_t bytesize = 0;
|
||||
@@ -1600,6 +1600,7 @@ static Buffer *luax_newbuffer(lua_State *L, int idx, const Buffer::Settings &set
|
||||
if (len <= 0)
|
||||
luaL_argerror(L, idx, "number of elements must be greater than 0");
|
||||
arraylength = (size_t) len;
|
||||
settings.zeroInitialize = true;
|
||||
}
|
||||
|
||||
Buffer *b = nullptr;
|
||||
@@ -1663,7 +1664,7 @@ static Buffer *luax_newbuffer(lua_State *L, int idx, const Buffer::Settings &set
|
||||
|
||||
int w_newBuffer(lua_State *L)
|
||||
{
|
||||
Buffer::Settings settings(0, Buffer::MAP_EXPLICIT_RANGE_MODIFY, BUFFERUSAGE_DYNAMIC);
|
||||
Buffer::Settings settings(0, BUFFERUSAGE_DYNAMIC);
|
||||
|
||||
luaL_checktype(L, 3, LUA_TTABLE);
|
||||
|
||||
@@ -1691,7 +1692,7 @@ int w_newBuffer(lua_State *L)
|
||||
|
||||
int w_newVertexBuffer(lua_State *L)
|
||||
{
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, Buffer::MAP_EXPLICIT_RANGE_MODIFY, BUFFERUSAGE_DYNAMIC);
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, BUFFERUSAGE_DYNAMIC);
|
||||
luax_optbuffersettings(L, 3, settings);
|
||||
|
||||
std::vector<Buffer::DataDeclaration> format;
|
||||
@@ -1706,7 +1707,7 @@ int w_newVertexBuffer(lua_State *L)
|
||||
|
||||
int w_newIndexBuffer(lua_State *L)
|
||||
{
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_INDEX, Buffer::MAP_EXPLICIT_RANGE_MODIFY, BUFFERUSAGE_DYNAMIC);
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_INDEX, BUFFERUSAGE_DYNAMIC);
|
||||
luax_optbuffersettings(L, 3, settings);
|
||||
|
||||
size_t arraylength = 0;
|
||||
@@ -1747,6 +1748,7 @@ int w_newIndexBuffer(lua_State *L)
|
||||
if (len <= 0)
|
||||
return luaL_argerror(L, 1, "number of elements must be greater than 0");
|
||||
arraylength = (size_t) len;
|
||||
settings.zeroInitialize = true;
|
||||
}
|
||||
|
||||
if (data != nullptr || !lua_isnoneornil(L, 2))
|
||||
@@ -1953,8 +1955,9 @@ static Mesh *newCustomMesh(lua_State *L)
|
||||
|
||||
luax_catchexcept(L, [&](){ t = instance()->newMesh(vertexformat, numvertices, drawmode, usage); });
|
||||
|
||||
// Maximum possible data size for a single vertex attribute.
|
||||
char data[sizeof(float) * 4];
|
||||
char *data = (char *) t->getVertexData();
|
||||
size_t stride = t->getVertexStride();
|
||||
const auto &members = t->getVertexFormat();
|
||||
|
||||
for (size_t vertindex = 0; vertindex < numvertices; vertindex++)
|
||||
{
|
||||
@@ -1965,7 +1968,8 @@ static Mesh *newCustomMesh(lua_State *L)
|
||||
int n = 0;
|
||||
for (size_t i = 0; i < vertexformat.size(); i++)
|
||||
{
|
||||
const auto &info = getDataFormatInfo(vertexformat[i].format);
|
||||
const auto &member = members[i];
|
||||
const auto &info = getDataFormatInfo(member.decl.format);
|
||||
|
||||
// get vertices[vertindex][n]
|
||||
for (int c = 0; c < info.components; c++)
|
||||
@@ -1974,20 +1978,18 @@ static Mesh *newCustomMesh(lua_State *L)
|
||||
lua_rawgeti(L, -(c + 1), n);
|
||||
}
|
||||
|
||||
size_t offset = vertindex * stride + member.offset;
|
||||
|
||||
// Fetch the values from Lua and store them in data buffer.
|
||||
luax_writebufferdata(L, -info.components, vertexformat[i].format, data);
|
||||
luax_writebufferdata(L, -info.components, member.decl.format, data + offset);
|
||||
|
||||
lua_pop(L, info.components);
|
||||
|
||||
luax_catchexcept(L,
|
||||
[&](){ t->setVertexAttribute(vertindex, i, data, sizeof(float) * 4); },
|
||||
[&](bool diderror){ if (diderror) t->release(); }
|
||||
);
|
||||
}
|
||||
|
||||
lua_pop(L, 1); // pop vertices[vertindex]
|
||||
}
|
||||
|
||||
t->setVertexDataModified(0, stride * numvertices);
|
||||
t->flush();
|
||||
}
|
||||
|
||||
|
||||
@@ -67,11 +67,13 @@ int w_Mesh_setVertices(lua_State *L)
|
||||
return luaL_error(L, "Too many vertices (expected at most %d, got %d)", totalverts - vertstart, vertcount);
|
||||
|
||||
size_t datasize = std::min(d->getSize(), vertcount * stride);
|
||||
char *bytedata = (char *) t->mapVertexData() + byteoffset;
|
||||
char *bytedata = (char *) t->getVertexData() + byteoffset;
|
||||
|
||||
memcpy(bytedata, d->getData(), datasize);
|
||||
|
||||
t->unmapVertexData(byteoffset, datasize);
|
||||
t->setVertexDataModified(byteoffset, datasize);
|
||||
t->flush();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -88,7 +90,7 @@ int w_Mesh_setVertices(lua_State *L)
|
||||
for (const Buffer::DataMember &member : vertexformat)
|
||||
ncomponents += member.info.components;
|
||||
|
||||
char *data = (char *) t->mapVertexData() + byteoffset;
|
||||
char *data = (char *) t->getVertexData() + byteoffset;
|
||||
|
||||
for (int i = 0; i < vertcount; i++)
|
||||
{
|
||||
@@ -114,7 +116,9 @@ int w_Mesh_setVertices(lua_State *L)
|
||||
data += stride;
|
||||
}
|
||||
|
||||
t->unmapVertexData(byteoffset, vertcount * stride);
|
||||
t->setVertexDataModified(byteoffset, vertcount * stride);
|
||||
t->flush();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -126,7 +130,10 @@ int w_Mesh_setVertex(lua_State *L)
|
||||
bool istable = lua_istable(L, 3);
|
||||
|
||||
const std::vector<Buffer::DataMember> &vertexformat = t->getVertexFormat();
|
||||
char *data = (char *) t->getVertexScratchBuffer();
|
||||
|
||||
char *data = nullptr;
|
||||
size_t offset = 0;
|
||||
luax_catchexcept(L, [&](){ data = (char *) t->checkVertexDataOffset(index, &offset); });
|
||||
|
||||
int idx = istable ? 1 : 3;
|
||||
|
||||
@@ -156,7 +163,7 @@ int w_Mesh_setVertex(lua_State *L)
|
||||
}
|
||||
}
|
||||
|
||||
luax_catchexcept(L, [&](){ t->setVertex(index, data, t->getVertexStride()); });
|
||||
t->setVertexDataModified(offset, t->getVertexStride());
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -167,9 +174,8 @@ int w_Mesh_getVertex(lua_State *L)
|
||||
|
||||
const std::vector<Buffer::DataMember> &vertexformat = t->getVertexFormat();
|
||||
|
||||
char *data = (char *) t->getVertexScratchBuffer();
|
||||
|
||||
luax_catchexcept(L, [&](){ t->getVertex(index, data, t->getVertexStride()); });
|
||||
const char *data = nullptr;
|
||||
luax_catchexcept(L, [&](){ data = (const char *) t->checkVertexDataOffset(index, nullptr); });
|
||||
|
||||
int n = 0;
|
||||
|
||||
@@ -195,13 +201,14 @@ int w_Mesh_setVertexAttribute(lua_State *L)
|
||||
|
||||
const Buffer::DataMember &member = vertexformat[attribindex];
|
||||
|
||||
// Maximum possible size for a single vertex attribute.
|
||||
char data[sizeof(float) * 4];
|
||||
char *data = nullptr;
|
||||
size_t offset = 0;
|
||||
luax_catchexcept(L, [&](){ data = (char *) t->checkVertexDataOffset(vertindex, &offset); });
|
||||
|
||||
// Fetch the values from Lua and store them in the data buffer.
|
||||
luax_writebufferdata(L, 4, member.decl.format, data);
|
||||
luax_writebufferdata(L, 4, member.decl.format, data + member.offset);
|
||||
|
||||
luax_catchexcept(L, [&](){ t->setVertexAttribute(vertindex, attribindex, data, sizeof(float) * 4); });
|
||||
t->setVertexDataModified(offset + member.offset, member.size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -218,12 +225,10 @@ int w_Mesh_getVertexAttribute(lua_State *L)
|
||||
|
||||
const Buffer::DataMember &member = vertexformat[attribindex];
|
||||
|
||||
// Maximum possible size for a single vertex attribute.
|
||||
char data[sizeof(float) * 4];
|
||||
const char *data = nullptr;
|
||||
luax_catchexcept(L, [&](){ data = (const char *) t->checkVertexDataOffset(vertindex, nullptr); });
|
||||
|
||||
luax_catchexcept(L, [&](){ t->getVertexAttribute(vertindex, attribindex, data, sizeof(float) * 4); });
|
||||
|
||||
luax_readbufferdata(L, member.decl.format, data);
|
||||
luax_readbufferdata(L, member.decl.format, data + member.offset);
|
||||
return member.info.components;
|
||||
}
|
||||
|
||||
@@ -290,17 +295,17 @@ int w_Mesh_attachAttribute(lua_State *L)
|
||||
const char *name = luaL_checkstring(L, 2);
|
||||
|
||||
Buffer *buffer = nullptr;
|
||||
Mesh *mesh = nullptr;
|
||||
if (luax_istype(L, 3, Buffer::type))
|
||||
{
|
||||
buffer = luax_checktype<Buffer>(L, 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
Mesh *mesh = luax_checkmesh(L, 3);
|
||||
mesh = luax_checkmesh(L, 3);
|
||||
buffer = mesh->getVertexBuffer();
|
||||
if (buffer == nullptr)
|
||||
return luaL_error(L, "Mesh does not have its own vertex buffer.");
|
||||
luax_markdeprecated(L, "Mesh:attachAttribute(name, mesh, ...)", API_METHOD, DEPRECATED_REPLACED, "Mesh:attachAttribute(name, buffer, ...)");
|
||||
}
|
||||
|
||||
AttributeStep step = STEP_PER_VERTEX;
|
||||
@@ -310,7 +315,7 @@ int w_Mesh_attachAttribute(lua_State *L)
|
||||
|
||||
const char *attachname = luaL_optstring(L, 5, name);
|
||||
|
||||
luax_catchexcept(L, [&](){ t->attachAttribute(name, buffer, attachname, step); });
|
||||
luax_catchexcept(L, [&](){ t->attachAttribute(name, buffer, mesh, attachname, step); });
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -218,20 +218,20 @@ int w_SpriteBatch_attachAttribute(lua_State *L)
|
||||
const char *name = luaL_checkstring(L, 2);
|
||||
|
||||
Buffer *buffer = nullptr;
|
||||
Mesh *mesh = nullptr;
|
||||
if (luax_istype(L, 3, Buffer::type))
|
||||
{
|
||||
buffer = luax_checktype<Buffer>(L, 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
Mesh *mesh = luax_checktype<Mesh>(L, 3);
|
||||
mesh = luax_checktype<Mesh>(L, 3);
|
||||
buffer = mesh->getVertexBuffer();
|
||||
if (buffer == nullptr)
|
||||
return luaL_error(L, "Mesh does not have its own vertex buffer.");
|
||||
luax_markdeprecated(L, "SpriteBatch:attachAttribute(name, mesh)", API_METHOD, DEPRECATED_REPLACED, "SpriteBatch:attachAttribute(name, buffer)");
|
||||
}
|
||||
|
||||
luax_catchexcept(L, [&](){ t->attachAttribute(name, buffer); });
|
||||
luax_catchexcept(L, [&](){ t->attachAttribute(name, buffer, mesh); });
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -65,6 +65,11 @@
|
||||
# include "graphics/Graphics.h"
|
||||
#endif
|
||||
|
||||
// For love::window::setHighDPIAllowed
|
||||
#ifdef LOVE_ENABLE_WINDOW
|
||||
# include "window/Window.h"
|
||||
#endif
|
||||
|
||||
// For love::audio::Audio::setMixWithSystem.
|
||||
#ifdef LOVE_ENABLE_AUDIO
|
||||
# include "audio/Audio.h"
|
||||
@@ -318,6 +323,14 @@ static int w__setGammaCorrect(lua_State *L)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int w__setHighDPIAllowed(lua_State *L)
|
||||
{
|
||||
#ifdef LOVE_ENABLE_WINDOW
|
||||
love::window::setHighDPIAllowed((bool) lua_toboolean(L, 1));
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int w__setAudioMixWithSystem(lua_State *L)
|
||||
{
|
||||
bool success = false;
|
||||
@@ -395,6 +408,9 @@ int luaopen_love(lua_State *L)
|
||||
lua_pushcfunction(L, w__setGammaCorrect);
|
||||
lua_setfield(L, -2, "_setGammaCorrect");
|
||||
|
||||
lua_pushcfunction(L, w__setHighDPIAllowed);
|
||||
lua_setfield(L, -2, "_setHighDPIAllowed");
|
||||
|
||||
// Exposed here because we need to be able to call it before the audio
|
||||
// module is initialized.
|
||||
lua_pushcfunction(L, w__setAudioMixWithSystem);
|
||||
|
||||
@@ -45,7 +45,7 @@ Body::Body(World *world, b2Vec2 p, Body::Type type)
|
||||
udata->ref = nullptr;
|
||||
b2BodyDef def;
|
||||
def.position = Physics::scaleDown(p);
|
||||
def.userData = (void *) udata;
|
||||
def.userData.pointer = (uintptr_t)udata;
|
||||
body = world->world->CreateBody(&def);
|
||||
// Box2D body holds a reference to the love Body.
|
||||
this->retain();
|
||||
@@ -385,9 +385,9 @@ void Body::setBullet(bool bullet)
|
||||
return body->SetBullet(bullet);
|
||||
}
|
||||
|
||||
bool Body::isActive() const
|
||||
bool Body::isEnabled() const
|
||||
{
|
||||
return body->IsActive();
|
||||
return body->IsEnabled();
|
||||
}
|
||||
|
||||
bool Body::isAwake() const
|
||||
@@ -405,9 +405,9 @@ bool Body::isSleepingAllowed() const
|
||||
return body->IsSleepingAllowed();
|
||||
}
|
||||
|
||||
void Body::setActive(bool active)
|
||||
void Body::setEnabled(bool enabled)
|
||||
{
|
||||
body->SetActive(active);
|
||||
body->SetEnabled(enabled);
|
||||
}
|
||||
|
||||
void Body::setAwake(bool awake)
|
||||
@@ -544,7 +544,7 @@ int Body::setUserData(lua_State *L)
|
||||
if (udata == nullptr)
|
||||
{
|
||||
udata = new bodyudata();
|
||||
body->SetUserData((void *) udata);
|
||||
body->GetUserData().pointer = (uintptr_t)udata;
|
||||
}
|
||||
|
||||
if(!udata->ref)
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#include "physics/Body.h"
|
||||
|
||||
// Box2D
|
||||
#include <Box2D/Box2D.h>
|
||||
#include <box2d/Box2D.h>
|
||||
|
||||
namespace love
|
||||
{
|
||||
@@ -360,7 +360,7 @@ public:
|
||||
* Checks whether a Body is active or not. An inactive body
|
||||
* cannot be interacted with.
|
||||
**/
|
||||
bool isActive() const;
|
||||
bool isEnabled() const;
|
||||
|
||||
/**
|
||||
* Checks whether a Body is awake or not. A Body
|
||||
@@ -377,7 +377,7 @@ public:
|
||||
/**
|
||||
* Changes the body's active state.
|
||||
**/
|
||||
void setActive(bool active);
|
||||
void setEnabled(bool enabled);
|
||||
|
||||
/**
|
||||
* Changes the body's sleep state.
|
||||
|
||||
@@ -47,56 +47,28 @@ void ChainShape::setNextVertex(float x, float y)
|
||||
{
|
||||
b2Vec2 v(x, y);
|
||||
b2ChainShape *c = (b2ChainShape *)shape;
|
||||
c->SetNextVertex(Physics::scaleDown(v));
|
||||
}
|
||||
|
||||
void ChainShape::setNextVertex()
|
||||
{
|
||||
b2ChainShape *c = (b2ChainShape *)shape;
|
||||
c->m_hasNextVertex = false;
|
||||
c->m_nextVertex = Physics::scaleDown(v);
|
||||
}
|
||||
|
||||
void ChainShape::setPreviousVertex(float x, float y)
|
||||
{
|
||||
b2Vec2 v(x, y);
|
||||
b2ChainShape *c = (b2ChainShape *)shape;
|
||||
c->SetPrevVertex(Physics::scaleDown(v));
|
||||
c->m_prevVertex = Physics::scaleDown(v);
|
||||
}
|
||||
|
||||
void ChainShape::setPreviousVertex()
|
||||
{
|
||||
b2ChainShape *c = (b2ChainShape *)shape;
|
||||
c->m_hasPrevVertex = false;
|
||||
}
|
||||
|
||||
bool ChainShape::getNextVertex(float &x, float &y) const
|
||||
b2Vec2 ChainShape::getNextVertex() const
|
||||
{
|
||||
b2ChainShape *c = (b2ChainShape *)shape;
|
||||
|
||||
if (c->m_hasNextVertex)
|
||||
{
|
||||
b2Vec2 v = Physics::scaleUp(c->m_nextVertex);
|
||||
x = v.x;
|
||||
y = v.y;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return Physics::scaleUp(c->m_nextVertex);
|
||||
}
|
||||
|
||||
bool ChainShape::getPreviousVertex(float &x, float &y) const
|
||||
b2Vec2 ChainShape::getPreviousVertex() const
|
||||
{
|
||||
b2ChainShape *c = (b2ChainShape *)shape;
|
||||
|
||||
if (c->m_hasPrevVertex)
|
||||
{
|
||||
b2Vec2 v = Physics::scaleUp(c->m_prevVertex);
|
||||
x = v.x;
|
||||
y = v.y;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return Physics::scaleUp(c->m_prevVertex);
|
||||
}
|
||||
|
||||
EdgeShape *ChainShape::getChildEdge(int index) const
|
||||
|
||||
@@ -56,7 +56,6 @@ public:
|
||||
* @param y The y-coordinate of the vertex.
|
||||
**/
|
||||
void setNextVertex(float x, float y);
|
||||
void setNextVertex();
|
||||
|
||||
/**
|
||||
* Establish connectivity to a vertex that precedes
|
||||
@@ -65,17 +64,16 @@ public:
|
||||
* @param y The y-coordinate of the vertex.
|
||||
**/
|
||||
void setPreviousVertex(float x, float y);
|
||||
void setPreviousVertex();
|
||||
|
||||
/**
|
||||
* Gets the vertex that follows the last vertex.
|
||||
**/
|
||||
bool getNextVertex(float &x, float &y) const;
|
||||
b2Vec2 getNextVertex() const;
|
||||
|
||||
/**
|
||||
* Gets the vertex that precedes the first vertex.
|
||||
**/
|
||||
bool getPreviousVertex(float &x, float &y) const;
|
||||
b2Vec2 getPreviousVertex() const;
|
||||
|
||||
/**
|
||||
* Returns a child EdgeShape.
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#include "World.h"
|
||||
|
||||
// Box2D
|
||||
#include <Box2D/Box2D.h>
|
||||
#include <box2d/Box2D.h>
|
||||
|
||||
namespace love
|
||||
{
|
||||
|
||||
@@ -58,22 +58,50 @@ float DistanceJoint::getLength() const
|
||||
|
||||
void DistanceJoint::setFrequency(float hz)
|
||||
{
|
||||
joint->SetFrequency(hz);
|
||||
float stiffness, damping;
|
||||
b2LinearStiffness(stiffness, damping, hz, getDampingRatio(), joint->GetBodyA(), joint->GetBodyB());
|
||||
joint->SetStiffness(stiffness);
|
||||
}
|
||||
|
||||
float DistanceJoint::getFrequency() const
|
||||
{
|
||||
return joint->GetFrequency();
|
||||
float frequency, ratio;
|
||||
Physics::b2LinearFrequency(frequency, ratio, joint->GetStiffness(), joint->GetDamping(), joint->GetBodyA(), joint->GetBodyB());
|
||||
return frequency;
|
||||
}
|
||||
|
||||
void DistanceJoint::setDampingRatio(float d)
|
||||
void DistanceJoint::setDampingRatio(float ratio)
|
||||
{
|
||||
joint->SetDampingRatio(d);
|
||||
float stiffness, damping;
|
||||
b2LinearStiffness(stiffness, damping, getFrequency(), ratio, joint->GetBodyA(), joint->GetBodyB());
|
||||
joint->SetDamping(damping);
|
||||
}
|
||||
|
||||
float DistanceJoint::getDampingRatio() const
|
||||
{
|
||||
return joint->GetDampingRatio();
|
||||
float frequency, ratio;
|
||||
Physics::b2LinearFrequency(frequency, ratio, joint->GetStiffness(), joint->GetDamping(), joint->GetBodyA(), joint->GetBodyB());
|
||||
return ratio;
|
||||
}
|
||||
|
||||
void DistanceJoint::setStiffness(float k)
|
||||
{
|
||||
joint->SetStiffness(k);
|
||||
}
|
||||
|
||||
float DistanceJoint::getStiffness() const
|
||||
{
|
||||
return joint->GetStiffness();
|
||||
}
|
||||
|
||||
void DistanceJoint::setDamping(float d)
|
||||
{
|
||||
joint->SetDamping(d);
|
||||
}
|
||||
|
||||
float DistanceJoint::getDamping() const
|
||||
{
|
||||
return joint->GetDamping();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -57,27 +57,45 @@ public:
|
||||
float getLength() const;
|
||||
|
||||
/**
|
||||
* Sets the response speed.
|
||||
* Sets the response speed. Independent of mass
|
||||
**/
|
||||
void setFrequency(float hz);
|
||||
|
||||
/**
|
||||
* Gets the response speed.
|
||||
* Gets the response speed. Independent of mass
|
||||
**/
|
||||
float getFrequency() const;
|
||||
|
||||
/**
|
||||
* Sets the damping ratio.
|
||||
* 0 = no damping, 1 = critical damping.
|
||||
* Set the spring damping ratio. Independent of mass
|
||||
**/
|
||||
void setDampingRatio(float d);
|
||||
void setDampingRatio(float ratio);
|
||||
|
||||
/**
|
||||
* Gets the damping ratio.
|
||||
* 0 = no damping, 1 = critical damping.
|
||||
* Get the spring damping ratio. Independent of mass
|
||||
**/
|
||||
float getDampingRatio() const;
|
||||
|
||||
/**
|
||||
* Sets the response speed. Dependent of mass
|
||||
**/
|
||||
void setStiffness(float k);
|
||||
|
||||
/**
|
||||
* Gets the response speed. Dependent of mass
|
||||
**/
|
||||
float getStiffness() const;
|
||||
|
||||
/**
|
||||
* Set the spring damping. Dependent of mass
|
||||
**/
|
||||
void setDamping(float ratio);
|
||||
|
||||
/**
|
||||
* Get the spring damping. Dependent of mass
|
||||
**/
|
||||
float getDamping() const;
|
||||
|
||||
private:
|
||||
// The Box2D DistanceJoint object.
|
||||
b2DistanceJoint *joint;
|
||||
|
||||
@@ -48,28 +48,13 @@ void EdgeShape::setNextVertex(float x, float y)
|
||||
b2EdgeShape *e = (b2EdgeShape *)shape;
|
||||
b2Vec2 v(x, y);
|
||||
e->m_vertex3 = Physics::scaleDown(v);
|
||||
e->m_hasVertex3 = true;
|
||||
}
|
||||
|
||||
void EdgeShape::setNextVertex()
|
||||
{
|
||||
b2EdgeShape *e = (b2EdgeShape *)shape;
|
||||
e->m_hasVertex3 = false;
|
||||
}
|
||||
|
||||
bool EdgeShape::getNextVertex(float &x, float &y) const
|
||||
b2Vec2 EdgeShape::getNextVertex() const
|
||||
{
|
||||
b2EdgeShape *e = (b2EdgeShape *)shape;
|
||||
|
||||
if (e->m_hasVertex3)
|
||||
{
|
||||
b2Vec2 v = Physics::scaleUp(e->m_vertex3);
|
||||
x = v.x;
|
||||
y = v.y;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return Physics::scaleUp(e->m_vertex3);
|
||||
}
|
||||
|
||||
void EdgeShape::setPreviousVertex(float x, float y)
|
||||
@@ -77,28 +62,13 @@ void EdgeShape::setPreviousVertex(float x, float y)
|
||||
b2EdgeShape *e = (b2EdgeShape *)shape;
|
||||
b2Vec2 v(x, y);
|
||||
e->m_vertex0 = Physics::scaleDown(v);
|
||||
e->m_hasVertex0 = true;
|
||||
}
|
||||
|
||||
void EdgeShape::setPreviousVertex()
|
||||
{
|
||||
b2EdgeShape *e = (b2EdgeShape *)shape;
|
||||
e->m_hasVertex0 = false;
|
||||
}
|
||||
|
||||
bool EdgeShape::getPreviousVertex(float &x, float &y) const
|
||||
b2Vec2 EdgeShape::getPreviousVertex() const
|
||||
{
|
||||
b2EdgeShape *e = (b2EdgeShape *)shape;
|
||||
|
||||
if (e->m_hasVertex0)
|
||||
{
|
||||
b2Vec2 v = Physics::scaleUp(e->m_vertex0);
|
||||
x = v.x;
|
||||
y = v.y;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return Physics::scaleUp(e->m_vertex0);
|
||||
}
|
||||
|
||||
int EdgeShape::getPoints(lua_State *L)
|
||||
|
||||
@@ -50,12 +50,10 @@ public:
|
||||
virtual ~EdgeShape();
|
||||
|
||||
void setNextVertex(float x, float y);
|
||||
void setNextVertex();
|
||||
bool getNextVertex(float &x, float &y) const;
|
||||
b2Vec2 getNextVertex() const;
|
||||
|
||||
void setPreviousVertex(float x, float y);
|
||||
void setPreviousVertex();
|
||||
bool getPreviousVertex(float &x, float &y) const;
|
||||
b2Vec2 getPreviousVertex() const;
|
||||
|
||||
/**
|
||||
* Returns the transformed points of the edge shape.
|
||||
|
||||
@@ -45,7 +45,7 @@ Fixture::Fixture(Body *body, Shape *shape, float density)
|
||||
udata->ref = nullptr;
|
||||
b2FixtureDef def;
|
||||
def.shape = shape->shape;
|
||||
def.userData = (void *)udata;
|
||||
def.userData.pointer = (uintptr_t)udata;
|
||||
def.density = density;
|
||||
fixture = body->body->CreateFixture(&def);
|
||||
this->retain();
|
||||
@@ -262,7 +262,7 @@ int Fixture::setUserData(lua_State *L)
|
||||
if (udata == nullptr)
|
||||
{
|
||||
udata = new fixtureudata();
|
||||
fixture->SetUserData((void *) udata);
|
||||
fixture->GetUserData().pointer = (uintptr_t)udata;
|
||||
}
|
||||
|
||||
if(!udata->ref)
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
#include "common/Reference.h"
|
||||
|
||||
// Box2D
|
||||
#include <Box2D/Box2D.h>
|
||||
#include <box2d/Box2D.h>
|
||||
|
||||
namespace love
|
||||
{
|
||||
|
||||
@@ -154,7 +154,7 @@ float Joint::getReactionTorque(float dt)
|
||||
|
||||
b2Joint *Joint::createJoint(b2JointDef *def)
|
||||
{
|
||||
def->userData = udata;
|
||||
def->userData.pointer = (uintptr_t)udata;
|
||||
joint = world->world->CreateJoint(def);
|
||||
world->registerObject(joint, this);
|
||||
// Box2D joint has a reference to this love Joint.
|
||||
@@ -185,9 +185,9 @@ void Joint::destroyJoint(bool implicit)
|
||||
this->release();
|
||||
}
|
||||
|
||||
bool Joint::isActive() const
|
||||
bool Joint::isEnabled() const
|
||||
{
|
||||
return joint->IsActive();
|
||||
return joint->IsEnabled();
|
||||
}
|
||||
|
||||
bool Joint::getCollideConnected() const
|
||||
@@ -202,7 +202,7 @@ int Joint::setUserData(lua_State *L)
|
||||
if (udata == nullptr)
|
||||
{
|
||||
udata = new jointudata();
|
||||
joint->SetUserData((void *) udata);
|
||||
joint->GetUserData().pointer = (uintptr_t)udata;
|
||||
}
|
||||
|
||||
if(!udata->ref)
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#include "physics/Joint.h"
|
||||
|
||||
// Box2D
|
||||
#include <Box2D/Box2D.h>
|
||||
#include <box2d/Box2D.h>
|
||||
|
||||
namespace love
|
||||
{
|
||||
@@ -103,7 +103,7 @@ public:
|
||||
**/
|
||||
float getReactionTorque(float dt);
|
||||
|
||||
bool isActive() const;
|
||||
bool isEnabled() const;
|
||||
|
||||
bool getCollideConnected() const;
|
||||
|
||||
|
||||
@@ -80,28 +80,62 @@ float MouseJoint::getMaxForce() const
|
||||
|
||||
void MouseJoint::setFrequency(float hz)
|
||||
{
|
||||
// This is kind of a crappy check. The frequency is used in an internal
|
||||
// This is kind of a crappy check. The Stiffness is used in an internal
|
||||
// box2d calculation whose result must be > FLT_EPSILON, but other variables
|
||||
// go into that calculation...
|
||||
if (hz <= FLT_EPSILON * 2)
|
||||
throw love::Exception("MouseJoint frequency must be a positive number.");
|
||||
throw love::Exception("MouseJoint Stiffness must be a positive number.");
|
||||
|
||||
joint->SetFrequency(hz);
|
||||
float stiffness, damping;
|
||||
b2LinearStiffness(stiffness, damping, hz, getDampingRatio(), joint->GetBodyA(), joint->GetBodyB());
|
||||
joint->SetStiffness(stiffness);
|
||||
}
|
||||
|
||||
float MouseJoint::getFrequency() const
|
||||
{
|
||||
return joint->GetFrequency();
|
||||
float frequency, ratio;
|
||||
Physics::b2LinearFrequency(frequency, ratio, joint->GetStiffness(), joint->GetDamping(), joint->GetBodyA(), joint->GetBodyB());
|
||||
return frequency;
|
||||
}
|
||||
|
||||
void MouseJoint::setDampingRatio(float d)
|
||||
void MouseJoint::setDampingRatio(float ratio)
|
||||
{
|
||||
joint->SetDampingRatio(d);
|
||||
float stiffness, damping;
|
||||
b2LinearStiffness(stiffness, damping, getFrequency(), ratio, joint->GetBodyA(), joint->GetBodyB());
|
||||
joint->SetDamping(damping);
|
||||
}
|
||||
|
||||
float MouseJoint::getDampingRatio() const
|
||||
{
|
||||
return joint->GetDampingRatio();
|
||||
float frequency, ratio;
|
||||
Physics::b2LinearFrequency(frequency, ratio, joint->GetStiffness(), joint->GetDamping(), joint->GetBodyA(), joint->GetBodyB());
|
||||
return ratio;
|
||||
}
|
||||
|
||||
void MouseJoint::setStiffness(float k)
|
||||
{
|
||||
// This is kind of a crappy check. The Stiffness is used in an internal
|
||||
// box2d calculation whose result must be > FLT_EPSILON, but other variables
|
||||
// go into that calculation...
|
||||
if (k <= FLT_EPSILON * 2)
|
||||
throw love::Exception("MouseJoint Stiffness must be a positive number.");
|
||||
|
||||
joint->SetStiffness(k);
|
||||
}
|
||||
|
||||
float MouseJoint::getStiffness() const
|
||||
{
|
||||
return joint->GetStiffness();
|
||||
}
|
||||
|
||||
void MouseJoint::setDamping(float d)
|
||||
{
|
||||
joint->SetDamping(d);
|
||||
}
|
||||
|
||||
float MouseJoint::getDamping() const
|
||||
{
|
||||
return joint->GetDamping();
|
||||
}
|
||||
|
||||
Body *MouseJoint::getBodyA() const
|
||||
|
||||
@@ -76,27 +76,45 @@ public:
|
||||
float getMaxForce() const;
|
||||
|
||||
/**
|
||||
* Sets the response speed.
|
||||
* Sets the response speed. Independent of mass
|
||||
**/
|
||||
void setFrequency(float hz);
|
||||
|
||||
/**
|
||||
* Gets the response speed.
|
||||
* Gets the response speed. Independent of mass
|
||||
**/
|
||||
float getFrequency() const;
|
||||
|
||||
/**
|
||||
* Sets the damping ratio.
|
||||
* 0 = no damping, 1 = critical damping.
|
||||
* Set the spring damping ratio. Independent of mass
|
||||
**/
|
||||
void setDampingRatio(float d);
|
||||
void setDampingRatio(float ratio);
|
||||
|
||||
/**
|
||||
* Gets the damping ratio.
|
||||
* 0 = no damping, 1 = critical damping.
|
||||
* Get the spring damping ratio. Independent of mass
|
||||
**/
|
||||
float getDampingRatio() const;
|
||||
|
||||
/**
|
||||
* Sets the response speed. Dependent of mass
|
||||
**/
|
||||
void setStiffness(float k);
|
||||
|
||||
/**
|
||||
* Gets the response speed. Dependent of mass
|
||||
**/
|
||||
float getStiffness() const;
|
||||
|
||||
/**
|
||||
* Set the spring damping. Dependent of mass
|
||||
**/
|
||||
void setDamping(float ratio);
|
||||
|
||||
/**
|
||||
* Get the spring damping. Dependent of mass
|
||||
**/
|
||||
float getDamping() const;
|
||||
|
||||
virtual Body *getBodyA() const;
|
||||
virtual Body *getBodyB() const;
|
||||
|
||||
|
||||
@@ -83,10 +83,19 @@ PolygonShape *Physics::newRectangleShape(float x, float y, float w, float h, flo
|
||||
return new PolygonShape(s);
|
||||
}
|
||||
|
||||
EdgeShape *Physics::newEdgeShape(float x1, float y1, float x2, float y2)
|
||||
EdgeShape *Physics::newEdgeShape(float x1, float y1, float x2, float y2, bool oneSided)
|
||||
{
|
||||
b2EdgeShape *s = new b2EdgeShape();
|
||||
s->Set(Physics::scaleDown(b2Vec2(x1, y1)), Physics::scaleDown(b2Vec2(x2, y2)));
|
||||
if (oneSided)
|
||||
{
|
||||
b2Vec2 v1 = Physics::scaleDown(b2Vec2(x1, y1));
|
||||
b2Vec2 v2 = Physics::scaleDown(b2Vec2(x2, y2));
|
||||
s->SetOneSided(v1, v1, v2, v2);
|
||||
}
|
||||
else
|
||||
{
|
||||
s->SetTwoSided(Physics::scaleDown(b2Vec2(x1, y1)), Physics::scaleDown(b2Vec2(x2, y2)));
|
||||
}
|
||||
return new EdgeShape(s);
|
||||
}
|
||||
|
||||
@@ -160,7 +169,7 @@ int Physics::newChainShape(lua_State *L)
|
||||
if (istable)
|
||||
argc = (int) luax_objlen(L, 2);
|
||||
|
||||
if (argc % 2 != 0)
|
||||
if (argc == 0 || argc % 2 != 0)
|
||||
return luaL_error(L, "Number of vertex components must be a multiple of two.");
|
||||
|
||||
int vcount = argc/2;
|
||||
@@ -196,7 +205,7 @@ int Physics::newChainShape(lua_State *L)
|
||||
if (loop)
|
||||
s->CreateLoop(vecs, vcount);
|
||||
else
|
||||
s->CreateChain(vecs, vcount);
|
||||
s->CreateChain(vecs, vcount, vecs[0], vecs[vcount-1]);
|
||||
}
|
||||
catch (love::Exception &)
|
||||
{
|
||||
@@ -386,6 +395,66 @@ b2AABB Physics::scaleUp(const b2AABB &aabb)
|
||||
return t;
|
||||
}
|
||||
|
||||
void Physics::b2LinearFrequency(float& frequency, float& ratio, float stiffness, float damping, b2Body* bodyA, b2Body* bodyB)
|
||||
{
|
||||
float massA = bodyA->GetMass();
|
||||
float massB = bodyB->GetMass();
|
||||
float mass;
|
||||
if (massA > 0.0f && massB > 0.0f)
|
||||
{
|
||||
mass = massA * massB / (massA + massB);
|
||||
}
|
||||
else if (massA > 0.0f)
|
||||
{
|
||||
mass = massA;
|
||||
}
|
||||
else
|
||||
{
|
||||
mass = massB;
|
||||
}
|
||||
|
||||
if (mass == 0.0f || stiffness <= 0.0f)
|
||||
{
|
||||
frequency = 0.0f;
|
||||
ratio = 0.0f;
|
||||
return;
|
||||
};
|
||||
|
||||
float omega = b2Sqrt(stiffness / mass);
|
||||
frequency = omega / (2.0f * b2_pi);
|
||||
ratio = damping / (mass * 2.0f * omega);
|
||||
}
|
||||
|
||||
void Physics::b2AngularFrequency(float& frequency, float& ratio, float stiffness, float damping, b2Body* bodyA, b2Body* bodyB)
|
||||
{
|
||||
float IA = bodyA->GetInertia();
|
||||
float IB = bodyB->GetInertia();
|
||||
float I;
|
||||
if (IA > 0.0f && IB > 0.0f)
|
||||
{
|
||||
I = IA * IB / (IA + IB);
|
||||
}
|
||||
else if (IA > 0.0f)
|
||||
{
|
||||
I = IA;
|
||||
}
|
||||
else
|
||||
{
|
||||
I = IB;
|
||||
}
|
||||
|
||||
if (I == 0.0f || stiffness <= 0.0f)
|
||||
{
|
||||
frequency = 0.0f;
|
||||
ratio = 0.0f;
|
||||
return;
|
||||
};
|
||||
|
||||
float omega = b2Sqrt(stiffness / I);
|
||||
frequency = omega / (2.0f * b2_pi);
|
||||
ratio = damping / (I * 2.0f * omega);
|
||||
}
|
||||
|
||||
} // box2d
|
||||
} // physics
|
||||
} // love
|
||||
|
||||
@@ -140,7 +140,7 @@ public:
|
||||
* @param x2 The x coordinate of the second point.
|
||||
* @param y2 The y coordinate of the second point.
|
||||
**/
|
||||
EdgeShape *newEdgeShape(float x1, float y1, float x2, float y2);
|
||||
EdgeShape *newEdgeShape(float x1, float y1, float x2, float y2, bool oneSided);
|
||||
|
||||
/**
|
||||
* Creates a new PolygonShape from a variable number of vertices.
|
||||
@@ -354,7 +354,29 @@ public:
|
||||
* @param aabb The unscaled input AABB.
|
||||
* @return The scaled AABB.
|
||||
**/
|
||||
static b2AABB scaleUp(const b2AABB &aabb);
|
||||
static b2AABB scaleUp(const b2AABB& aabb);
|
||||
|
||||
/**
|
||||
* Calculates linear frequency and damping radio from stiffness and damping
|
||||
* @param frequency The output frequency
|
||||
* @param ratio The output damping ratio
|
||||
* @param stiffness The joint stiffness
|
||||
* @param damping The joint damping
|
||||
* @param bodyA The bodyA of the joint
|
||||
* @param bodyB The bodyB of the joint
|
||||
**/
|
||||
static void b2LinearFrequency(float& frequency, float& ratio, float stiffness, float damping, b2Body* bodyA, b2Body* bodyB);
|
||||
|
||||
/**
|
||||
* Calculates angular frequency and damping radio from stiffness and damping
|
||||
* @param frequency The output frequency
|
||||
* @param ratio The output damping ratio
|
||||
* @param stiffness The joint stiffness
|
||||
* @param damping The joint damping
|
||||
* @param bodyA The bodyA of the joint
|
||||
* @param bodyB The bodyB of the joint
|
||||
**/
|
||||
static void b2AngularFrequency(float& frequency, float& ratio, float stiffness, float damping, b2Body* bodyA, b2Body* bodyB);
|
||||
|
||||
private:
|
||||
|
||||
|
||||
@@ -47,10 +47,10 @@ int PolygonShape::getPoints(lua_State *L)
|
||||
{
|
||||
love::luax_assert_argc(L, 0);
|
||||
b2PolygonShape *p = (b2PolygonShape *)shape;
|
||||
int count = p->GetVertexCount();
|
||||
int count = p->m_count;
|
||||
for (int i = 0; i<count; i++)
|
||||
{
|
||||
b2Vec2 v = Physics::scaleUp(p->GetVertex(i));
|
||||
b2Vec2 v = Physics::scaleUp(p->m_vertices[i]);
|
||||
lua_pushnumber(L, v.x);
|
||||
lua_pushnumber(L, v.y);
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ RopeJoint::RopeJoint(Body *body1, Body *body2, float x1, float y1, float x2, flo
|
||||
: Joint(body1, body2)
|
||||
, joint(NULL)
|
||||
{
|
||||
b2RopeJointDef def;
|
||||
b2DistanceJointDef def;
|
||||
def.bodyA = body1->body;
|
||||
def.bodyB = body2->body;
|
||||
body1->getLocalPoint(x1, y1, x1, y1);
|
||||
@@ -49,7 +49,7 @@ RopeJoint::RopeJoint(Body *body1, Body *body2, float x1, float y1, float x2, flo
|
||||
def.localAnchorB.y = Physics::scaleDown(y2);
|
||||
def.maxLength = Physics::scaleDown(maxLength);
|
||||
def.collideConnected = collideConnected;
|
||||
joint = (b2RopeJoint *)createJoint(&def);
|
||||
joint = (b2DistanceJoint *)createJoint(&def);
|
||||
}
|
||||
|
||||
RopeJoint::~RopeJoint()
|
||||
|
||||
@@ -56,7 +56,7 @@ public:
|
||||
|
||||
private:
|
||||
// The Box2D RopeJoint object.
|
||||
b2RopeJoint *joint;
|
||||
b2DistanceJoint *joint;
|
||||
};
|
||||
|
||||
} // box2d
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#include "physics/box2d/Body.h"
|
||||
|
||||
// Box2D
|
||||
#include <Box2D/Box2D.h>
|
||||
#include <box2d/Box2D.h>
|
||||
|
||||
namespace love
|
||||
{
|
||||
|
||||
@@ -68,22 +68,50 @@ void WeldJoint::init(b2WeldJointDef &def, Body *body1, Body *body2, float xA, fl
|
||||
|
||||
void WeldJoint::setFrequency(float hz)
|
||||
{
|
||||
joint->SetFrequency(hz);
|
||||
float stiffness, damping;
|
||||
b2LinearStiffness(stiffness, damping, hz, getDampingRatio(), joint->GetBodyA(), joint->GetBodyB());
|
||||
joint->SetStiffness(stiffness);
|
||||
}
|
||||
|
||||
float WeldJoint::getFrequency() const
|
||||
{
|
||||
return joint->GetFrequency();
|
||||
float frequency, ratio;
|
||||
Physics::b2LinearFrequency(frequency, ratio, joint->GetStiffness(), joint->GetDamping(), joint->GetBodyA(), joint->GetBodyB());
|
||||
return frequency;
|
||||
}
|
||||
|
||||
void WeldJoint::setDampingRatio(float d)
|
||||
void WeldJoint::setDampingRatio(float ratio)
|
||||
{
|
||||
joint->SetDampingRatio(d);
|
||||
float stiffness, damping;
|
||||
b2LinearStiffness(stiffness, damping, getFrequency(), ratio, joint->GetBodyA(), joint->GetBodyB());
|
||||
joint->SetDamping(damping);
|
||||
}
|
||||
|
||||
float WeldJoint::getDampingRatio() const
|
||||
{
|
||||
return joint->GetDampingRatio();
|
||||
float frequency, ratio;
|
||||
Physics::b2LinearFrequency(frequency, ratio, joint->GetStiffness(), joint->GetDamping(), joint->GetBodyA(), joint->GetBodyB());
|
||||
return ratio;
|
||||
}
|
||||
|
||||
void WeldJoint::setStiffness(float k)
|
||||
{
|
||||
joint->SetStiffness(k);
|
||||
}
|
||||
|
||||
float WeldJoint::getStiffness() const
|
||||
{
|
||||
return joint->GetStiffness();
|
||||
}
|
||||
|
||||
void WeldJoint::setDamping(float d)
|
||||
{
|
||||
joint->SetDamping(d);
|
||||
}
|
||||
|
||||
float WeldJoint::getDamping() const
|
||||
{
|
||||
return joint->GetDamping();
|
||||
}
|
||||
|
||||
float WeldJoint::getReferenceAngle() const
|
||||
|
||||
@@ -50,27 +50,45 @@ public:
|
||||
virtual ~WeldJoint();
|
||||
|
||||
/**
|
||||
* Sets the response speed.
|
||||
* Sets the response speed. Independent of mass
|
||||
**/
|
||||
void setFrequency(float hz);
|
||||
|
||||
/**
|
||||
* Gets the response speed.
|
||||
* Gets the response speed. Independent of mass
|
||||
**/
|
||||
float getFrequency() const;
|
||||
|
||||
/**
|
||||
* Sets the damping ratio.
|
||||
* 0 = no damping, 1 = critical damping.
|
||||
* Set the spring damping ratio. Independent of mass
|
||||
**/
|
||||
void setDampingRatio(float d);
|
||||
void setDampingRatio(float ratio);
|
||||
|
||||
/**
|
||||
* Gets the damping ratio.
|
||||
* 0 = no damping, 1 = critical damping.
|
||||
* Get the spring damping ratio. Independent of mass
|
||||
**/
|
||||
float getDampingRatio() const;
|
||||
|
||||
/**
|
||||
* Sets the response speed. Dependent of mass
|
||||
**/
|
||||
void setStiffness(float k);
|
||||
|
||||
/**
|
||||
* Gets the response speed. Dependent of mass
|
||||
**/
|
||||
float getStiffness() const;
|
||||
|
||||
/**
|
||||
* Set the spring damping. Dependent of mass
|
||||
**/
|
||||
void setDamping(float ratio);
|
||||
|
||||
/**
|
||||
* Get the spring damping. Dependent of mass
|
||||
**/
|
||||
float getDamping() const;
|
||||
|
||||
/**
|
||||
* Gets the reference angle.
|
||||
**/
|
||||
|
||||
@@ -57,7 +57,7 @@ float WheelJoint::getJointTranslation() const
|
||||
|
||||
float WheelJoint::getJointSpeed() const
|
||||
{
|
||||
return Physics::scaleUp(joint->GetJointSpeed());
|
||||
return Physics::scaleUp(joint->GetJointLinearSpeed());
|
||||
}
|
||||
|
||||
void WheelJoint::setMotorEnabled(bool enable)
|
||||
@@ -95,24 +95,52 @@ float WheelJoint::getMotorTorque(float inv_dt) const
|
||||
return Physics::scaleUp(Physics::scaleUp(joint->GetMotorTorque(inv_dt)));
|
||||
}
|
||||
|
||||
void WheelJoint::setSpringFrequency(float hz)
|
||||
void WheelJoint::setFrequency(float hz)
|
||||
{
|
||||
joint->SetSpringFrequencyHz(hz);
|
||||
float stiffness, damping;
|
||||
b2AngularStiffness(stiffness, damping, hz, getDampingRatio(), joint->GetBodyA(), joint->GetBodyB());
|
||||
joint->SetStiffness(stiffness);
|
||||
}
|
||||
|
||||
float WheelJoint::getSpringFrequency() const
|
||||
float WheelJoint::getFrequency() const
|
||||
{
|
||||
return joint->GetSpringFrequencyHz();
|
||||
float frequency, ratio;
|
||||
Physics::b2AngularFrequency(frequency, ratio, joint->GetStiffness(), joint->GetDamping(), joint->GetBodyA(), joint->GetBodyB());
|
||||
return frequency;
|
||||
}
|
||||
|
||||
void WheelJoint::setSpringDampingRatio(float ratio)
|
||||
void WheelJoint::setDampingRatio(float ratio)
|
||||
{
|
||||
joint->SetSpringDampingRatio(ratio);
|
||||
float stiffness, damping;
|
||||
b2AngularStiffness(stiffness, damping, getFrequency(), ratio, joint->GetBodyA(), joint->GetBodyB());
|
||||
joint->SetDamping(damping);
|
||||
}
|
||||
|
||||
float WheelJoint::getSpringDampingRatio() const
|
||||
float WheelJoint::getDampingRatio() const
|
||||
{
|
||||
return joint->GetSpringDampingRatio();
|
||||
float frequency, ratio;
|
||||
Physics::b2AngularFrequency(frequency, ratio, joint->GetStiffness(), joint->GetDamping(), joint->GetBodyA(), joint->GetBodyB());
|
||||
return ratio;
|
||||
}
|
||||
|
||||
void WheelJoint::setStiffness(float k)
|
||||
{
|
||||
joint->SetStiffness(k);
|
||||
}
|
||||
|
||||
float WheelJoint::getStiffness() const
|
||||
{
|
||||
return joint->GetStiffness();
|
||||
}
|
||||
|
||||
void WheelJoint::setDamping(float ratio)
|
||||
{
|
||||
joint->SetDamping(ratio);
|
||||
}
|
||||
|
||||
float WheelJoint::getDamping() const
|
||||
{
|
||||
return joint->GetDamping();
|
||||
}
|
||||
|
||||
int WheelJoint::getAxis(lua_State *L)
|
||||
|
||||
@@ -96,25 +96,44 @@ public:
|
||||
float getMotorTorque(float inv_dt) const;
|
||||
|
||||
/**
|
||||
* Set the spring frequency, in hertz. Setting the frequency to 0
|
||||
* disables the spring.
|
||||
* Sets the response speed. Independent of mass
|
||||
**/
|
||||
void setSpringFrequency(float hz);
|
||||
void setFrequency(float hz);
|
||||
|
||||
/**
|
||||
* Get the spring frequency, in hertz.
|
||||
* Gets the response speed. Independent of mass
|
||||
**/
|
||||
float getSpringFrequency() const;
|
||||
float getFrequency() const;
|
||||
|
||||
/**
|
||||
* Set the spring damping ratio.
|
||||
* Set the spring damping ratio. Independent of mass
|
||||
**/
|
||||
void setSpringDampingRatio(float ratio);
|
||||
void setDampingRatio(float ratio);
|
||||
|
||||
/**
|
||||
* Get the spring damping ratio.
|
||||
* Get the spring damping ratio. Independent of mass
|
||||
**/
|
||||
float getSpringDampingRatio() const;
|
||||
float getDampingRatio() const;
|
||||
|
||||
/**
|
||||
* Sets the response speed. Dependent of mass
|
||||
**/
|
||||
void setStiffness(float k);
|
||||
|
||||
/**
|
||||
* Gets the response speed. Dependent of mass
|
||||
**/
|
||||
float getStiffness() const;
|
||||
|
||||
/**
|
||||
* Set the spring damping. Dependent of mass
|
||||
**/
|
||||
void setDamping(float ratio);
|
||||
|
||||
/**
|
||||
* Get the spring damping. Dependent of mass
|
||||
**/
|
||||
float getDamping() const;
|
||||
|
||||
/**
|
||||
* Gets the axis unit vector, relative to body1.
|
||||
|
||||
@@ -183,7 +183,7 @@ World::RayCastCallback::~RayCastCallback()
|
||||
{
|
||||
}
|
||||
|
||||
float32 World::RayCastCallback::ReportFixture(b2Fixture *fixture, const b2Vec2 &point, const b2Vec2 &normal, float32 fraction)
|
||||
float World::RayCastCallback::ReportFixture(b2Fixture *fixture, const b2Vec2 &point, const b2Vec2 &normal, float fraction)
|
||||
{
|
||||
if (L != nullptr)
|
||||
{
|
||||
@@ -201,7 +201,7 @@ float32 World::RayCastCallback::ReportFixture(b2Fixture *fixture, const b2Vec2 &
|
||||
lua_call(L, 6, 1);
|
||||
if (!lua_isnumber(L, -1))
|
||||
luaL_error(L, "Raycast callback didn't return a number!");
|
||||
float32 fraction = (float32) lua_tonumber(L, -1);
|
||||
float fraction = (float) lua_tonumber(L, -1);
|
||||
lua_pop(L, 1);
|
||||
return fraction;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
#include <unordered_map>
|
||||
|
||||
// Box2D
|
||||
#include <Box2D/Box2D.h>
|
||||
#include <box2d/Box2D.h>
|
||||
|
||||
namespace love
|
||||
{
|
||||
@@ -107,7 +107,7 @@ public:
|
||||
public:
|
||||
RayCastCallback(World *world, lua_State *L, int idx);
|
||||
~RayCastCallback();
|
||||
virtual float32 ReportFixture(b2Fixture *fixture, const b2Vec2 &point, const b2Vec2 &normal, float32 fraction);
|
||||
virtual float ReportFixture(b2Fixture *fixture, const b2Vec2 &point, const b2Vec2 &normal, float fraction);
|
||||
private:
|
||||
World *world;
|
||||
lua_State *L;
|
||||
|
||||
@@ -518,7 +518,7 @@ int w_Body_setBullet(lua_State *L)
|
||||
int w_Body_isActive(lua_State *L)
|
||||
{
|
||||
Body *t = luax_checkbody(L, 1);
|
||||
luax_pushboolean(L, t->isActive());
|
||||
luax_pushboolean(L, t->isEnabled());
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -548,7 +548,7 @@ int w_Body_setActive(lua_State *L)
|
||||
{
|
||||
Body *t = luax_checkbody(L, 1);
|
||||
bool b = luax_checkboolean(L, 2);
|
||||
luax_catchexcept(L, [&](){ t->setActive(b); });
|
||||
luax_catchexcept(L, [&](){ t->setEnabled(b); });
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,28 +37,18 @@ ChainShape *luax_checkchainshape(lua_State *L, int idx)
|
||||
int w_ChainShape_setNextVertex(lua_State *L)
|
||||
{
|
||||
ChainShape *c = luax_checkchainshape(L, 1);
|
||||
if (lua_isnoneornil(L, 2))
|
||||
c->setNextVertex();
|
||||
else
|
||||
{
|
||||
float x = (float)luaL_checknumber(L, 2);
|
||||
float y = (float)luaL_checknumber(L, 3);
|
||||
luax_catchexcept(L, [&](){ c->setNextVertex(x, y); });
|
||||
}
|
||||
float x = (float)luaL_checknumber(L, 2);
|
||||
float y = (float)luaL_checknumber(L, 3);
|
||||
luax_catchexcept(L, [&](){ c->setNextVertex(x, y); });
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_ChainShape_setPreviousVertex(lua_State *L)
|
||||
{
|
||||
ChainShape *c = luax_checkchainshape(L, 1);
|
||||
if (lua_isnoneornil(L, 2))
|
||||
c->setPreviousVertex();
|
||||
else
|
||||
{
|
||||
float x = (float)luaL_checknumber(L, 2);
|
||||
float y = (float)luaL_checknumber(L, 3);
|
||||
luax_catchexcept(L, [&](){ c->setPreviousVertex(x, y); });
|
||||
}
|
||||
float x = (float)luaL_checknumber(L, 2);
|
||||
float y = (float)luaL_checknumber(L, 3);
|
||||
luax_catchexcept(L, [&](){ c->setPreviousVertex(x, y); });
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -95,27 +85,19 @@ int w_ChainShape_getPoint(lua_State *L)
|
||||
int w_ChainShape_getNextVertex(lua_State *L)
|
||||
{
|
||||
ChainShape *c = luax_checkchainshape(L, 1);
|
||||
float x, y;
|
||||
if (c->getNextVertex(x, y))
|
||||
{
|
||||
lua_pushnumber(L, x);
|
||||
lua_pushnumber(L, y);
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
b2Vec2 v = c->getNextVertex();
|
||||
lua_pushnumber(L, v.x);
|
||||
lua_pushnumber(L, v.y);
|
||||
return 2;
|
||||
}
|
||||
|
||||
int w_ChainShape_getPreviousVertex(lua_State *L)
|
||||
{
|
||||
ChainShape *c = luax_checkchainshape(L, 1);
|
||||
float x, y;
|
||||
if (c->getPreviousVertex(x, y))
|
||||
{
|
||||
lua_pushnumber(L, x);
|
||||
lua_pushnumber(L, y);
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
b2Vec2 v = c->getPreviousVertex();
|
||||
lua_pushnumber(L, v.x);
|
||||
lua_pushnumber(L, v.y);
|
||||
return 2;
|
||||
}
|
||||
|
||||
int w_ChainShape_getPoints(lua_State *L)
|
||||
|
||||
@@ -80,6 +80,36 @@ int w_DistanceJoint_getDampingRatio(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_DistanceJoint_setStiffness(lua_State *L)
|
||||
{
|
||||
DistanceJoint *t = luax_checkdistancejoint(L, 1);
|
||||
float arg1 = (float)luaL_checknumber(L, 2);
|
||||
t->setStiffness(arg1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_DistanceJoint_getStiffness(lua_State *L)
|
||||
{
|
||||
DistanceJoint *t = luax_checkdistancejoint(L, 1);
|
||||
lua_pushnumber(L, t->getStiffness());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_DistanceJoint_setDamping(lua_State *L)
|
||||
{
|
||||
DistanceJoint *t = luax_checkdistancejoint(L, 1);
|
||||
float arg1 = (float)luaL_checknumber(L, 2);
|
||||
t->setDamping(arg1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_DistanceJoint_getDamping(lua_State *L)
|
||||
{
|
||||
DistanceJoint *t = luax_checkdistancejoint(L, 1);
|
||||
lua_pushnumber(L, t->getDamping());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const luaL_Reg w_DistanceJoint_functions[] =
|
||||
{
|
||||
{ "setLength", w_DistanceJoint_setLength },
|
||||
@@ -88,6 +118,10 @@ static const luaL_Reg w_DistanceJoint_functions[] =
|
||||
{ "getFrequency", w_DistanceJoint_getFrequency },
|
||||
{ "setDampingRatio", w_DistanceJoint_setDampingRatio },
|
||||
{ "getDampingRatio", w_DistanceJoint_getDampingRatio },
|
||||
{ "setStiffness", w_DistanceJoint_setStiffness },
|
||||
{ "getStiffness", w_DistanceJoint_getStiffness },
|
||||
{ "setDamping", w_DistanceJoint_setDamping },
|
||||
{ "getDamping", w_DistanceJoint_getDamping },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
|
||||
@@ -35,55 +35,37 @@ EdgeShape *luax_checkedgeshape(lua_State *L, int idx)
|
||||
int w_EdgeShape_setNextVertex(lua_State *L)
|
||||
{
|
||||
EdgeShape *t = luax_checkedgeshape(L, 1);
|
||||
if (lua_isnoneornil(L, 2))
|
||||
t->setNextVertex();
|
||||
else
|
||||
{
|
||||
float x = (float)luaL_checknumber(L, 2);
|
||||
float y = (float)luaL_checknumber(L, 3);
|
||||
t->setNextVertex(x, y);
|
||||
}
|
||||
float x = (float)luaL_checknumber(L, 2);
|
||||
float y = (float)luaL_checknumber(L, 3);
|
||||
t->setNextVertex(x, y);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_EdgeShape_setPreviousVertex(lua_State *L)
|
||||
{
|
||||
EdgeShape *t = luax_checkedgeshape(L, 1);
|
||||
if (lua_isnoneornil(L, 2))
|
||||
t->setPreviousVertex();
|
||||
else
|
||||
{
|
||||
float x = (float)luaL_checknumber(L, 2);
|
||||
float y = (float)luaL_checknumber(L, 3);
|
||||
t->setPreviousVertex(x, y);
|
||||
}
|
||||
float x = (float)luaL_checknumber(L, 2);
|
||||
float y = (float)luaL_checknumber(L, 3);
|
||||
t->setPreviousVertex(x, y);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_EdgeShape_getNextVertex(lua_State *L)
|
||||
{
|
||||
EdgeShape *t = luax_checkedgeshape(L, 1);
|
||||
float x, y;
|
||||
if (t->getNextVertex(x, y))
|
||||
{
|
||||
lua_pushnumber(L, x);
|
||||
lua_pushnumber(L, y);
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
b2Vec2 v = t->getNextVertex();
|
||||
lua_pushnumber(L, v.x);
|
||||
lua_pushnumber(L, v.y);
|
||||
return 2;
|
||||
}
|
||||
|
||||
int w_EdgeShape_getPreviousVertex(lua_State *L)
|
||||
{
|
||||
EdgeShape *t = luax_checkedgeshape(L, 1);
|
||||
float x, y;
|
||||
if (t->getPreviousVertex(x, y))
|
||||
{
|
||||
lua_pushnumber(L, x);
|
||||
lua_pushnumber(L, y);
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
b2Vec2 v = t->getPreviousVertex();
|
||||
lua_pushnumber(L, v.x);
|
||||
lua_pushnumber(L, v.y);
|
||||
return 2;
|
||||
}
|
||||
|
||||
int w_EdgeShape_getPoints(lua_State *L)
|
||||
|
||||
@@ -96,6 +96,36 @@ int w_MouseJoint_getDampingRatio(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_MouseJoint_setStiffness(lua_State *L)
|
||||
{
|
||||
MouseJoint *t = luax_checkmousejoint(L, 1);
|
||||
float arg1 = (float)luaL_checknumber(L, 2);
|
||||
luax_catchexcept(L, [&]() { t->setStiffness(arg1); });
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_MouseJoint_getStiffness(lua_State *L)
|
||||
{
|
||||
MouseJoint *t = luax_checkmousejoint(L, 1);
|
||||
lua_pushnumber(L, t->getStiffness());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_MouseJoint_setDamping(lua_State *L)
|
||||
{
|
||||
MouseJoint *t = luax_checkmousejoint(L, 1);
|
||||
float arg1 = (float)luaL_checknumber(L, 2);
|
||||
t->setDamping(arg1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_MouseJoint_getDamping(lua_State *L)
|
||||
{
|
||||
MouseJoint *t = luax_checkmousejoint(L, 1);
|
||||
lua_pushnumber(L, t->getDamping());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const luaL_Reg w_MouseJoint_functions[] =
|
||||
{
|
||||
{ "setTarget", w_MouseJoint_setTarget },
|
||||
@@ -106,6 +136,10 @@ static const luaL_Reg w_MouseJoint_functions[] =
|
||||
{ "getFrequency", w_MouseJoint_getFrequency },
|
||||
{ "setDampingRatio", w_MouseJoint_setDampingRatio },
|
||||
{ "getDampingRatio", w_MouseJoint_getDampingRatio },
|
||||
{ "setStiffness", w_MouseJoint_setStiffness },
|
||||
{ "getStiffness", w_MouseJoint_getStiffness },
|
||||
{ "setDamping", w_MouseJoint_setDamping },
|
||||
{ "getDamping", w_MouseJoint_getDamping },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
|
||||
@@ -160,8 +160,9 @@ int w_newEdgeShape(lua_State *L)
|
||||
float y1 = (float)luaL_checknumber(L, 2);
|
||||
float x2 = (float)luaL_checknumber(L, 3);
|
||||
float y2 = (float)luaL_checknumber(L, 4);
|
||||
bool oneSided = luax_optboolean(L, 5, false);
|
||||
EdgeShape *shape;
|
||||
luax_catchexcept(L, [&](){ shape = instance()->newEdgeShape(x1, y1, x2, y2); });
|
||||
luax_catchexcept(L, [&](){ shape = instance()->newEdgeShape(x1, y1, x2, y2, oneSided); });
|
||||
luax_pushtype(L, shape);
|
||||
shape->release();
|
||||
return 1;
|
||||
|
||||
@@ -65,6 +65,36 @@ int w_WeldJoint_getDampingRatio(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_WeldJoint_setStiffness(lua_State *L)
|
||||
{
|
||||
WeldJoint *t = luax_checkweldjoint(L, 1);
|
||||
float arg1 = (float)luaL_checknumber(L, 2);
|
||||
t->setStiffness(arg1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_WeldJoint_getStiffness(lua_State *L)
|
||||
{
|
||||
WeldJoint *t = luax_checkweldjoint(L, 1);
|
||||
lua_pushnumber(L, t->getStiffness());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_WeldJoint_setDamping(lua_State *L)
|
||||
{
|
||||
WeldJoint *t = luax_checkweldjoint(L, 1);
|
||||
float arg1 = (float)luaL_checknumber(L, 2);
|
||||
t->setDamping(arg1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_WeldJoint_getDamping(lua_State *L)
|
||||
{
|
||||
WeldJoint *t = luax_checkweldjoint(L, 1);
|
||||
lua_pushnumber(L, t->getDamping());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_WeldJoint_getReferenceAngle(lua_State *L)
|
||||
{
|
||||
WeldJoint *t = luax_checkweldjoint(L, 1);
|
||||
@@ -78,6 +108,10 @@ static const luaL_Reg w_WeldJoint_functions[] =
|
||||
{ "getFrequency", w_WeldJoint_getFrequency },
|
||||
{ "setDampingRatio", w_WeldJoint_setDampingRatio },
|
||||
{ "getDampingRatio", w_WeldJoint_getDampingRatio },
|
||||
{ "setStiffness", w_WeldJoint_setStiffness },
|
||||
{ "getStiffness", w_WeldJoint_getStiffness },
|
||||
{ "setDamping", w_WeldJoint_setDamping },
|
||||
{ "getDamping", w_WeldJoint_getDamping },
|
||||
{ "getReferenceAngle", w_WeldJoint_getReferenceAngle },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
@@ -102,33 +102,63 @@ int w_WheelJoint_getMotorTorque(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_WheelJoint_setSpringFrequency(lua_State *L)
|
||||
int w_WheelJoint_setFrequency(lua_State *L)
|
||||
{
|
||||
WheelJoint *t = luax_checkwheeljoint(L, 1);
|
||||
float arg1 = (float)luaL_checknumber(L, 2);
|
||||
t->setSpringFrequency(arg1);
|
||||
t->setFrequency(arg1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_WheelJoint_getSpringFrequency(lua_State *L)
|
||||
int w_WheelJoint_getFrequency(lua_State *L)
|
||||
{
|
||||
WheelJoint *t = luax_checkwheeljoint(L, 1);
|
||||
lua_pushnumber(L, t->getSpringFrequency());
|
||||
lua_pushnumber(L, t->getFrequency());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_WheelJoint_setSpringDampingRatio(lua_State *L)
|
||||
int w_WheelJoint_setDampingRatio(lua_State *L)
|
||||
{
|
||||
WheelJoint *t = luax_checkwheeljoint(L, 1);
|
||||
float arg1 = (float)luaL_checknumber(L, 2);
|
||||
t->setSpringDampingRatio(arg1);
|
||||
t->setDampingRatio(arg1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_WheelJoint_getSpringDampingRatio(lua_State *L)
|
||||
int w_WheelJoint_getDampingRatio(lua_State *L)
|
||||
{
|
||||
WheelJoint *t = luax_checkwheeljoint(L, 1);
|
||||
lua_pushnumber(L, t->getSpringDampingRatio());
|
||||
lua_pushnumber(L, t->getDampingRatio());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_WheelJoint_setStiffness(lua_State *L)
|
||||
{
|
||||
WheelJoint *t = luax_checkwheeljoint(L, 1);
|
||||
float arg1 = (float)luaL_checknumber(L, 2);
|
||||
t->setStiffness(arg1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_WheelJoint_getStiffness(lua_State *L)
|
||||
{
|
||||
WheelJoint *t = luax_checkwheeljoint(L, 1);
|
||||
lua_pushnumber(L, t->getStiffness());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_WheelJoint_setDamping(lua_State *L)
|
||||
{
|
||||
WheelJoint *t = luax_checkwheeljoint(L, 1);
|
||||
float arg1 = (float)luaL_checknumber(L, 2);
|
||||
t->setDamping(arg1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_WheelJoint_getDamping(lua_State *L)
|
||||
{
|
||||
WheelJoint *t = luax_checkwheeljoint(L, 1);
|
||||
lua_pushnumber(L, t->getDamping());
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -150,10 +180,22 @@ static const luaL_Reg w_WheelJoint_functions[] =
|
||||
{ "setMaxMotorTorque", w_WheelJoint_setMaxMotorTorque },
|
||||
{ "getMaxMotorTorque", w_WheelJoint_getMaxMotorTorque },
|
||||
{ "getMotorTorque", w_WheelJoint_getMotorTorque },
|
||||
{ "setSpringFrequency", w_WheelJoint_setSpringFrequency },
|
||||
{ "getSpringFrequency", w_WheelJoint_getSpringFrequency },
|
||||
{ "setSpringDampingRatio", w_WheelJoint_setSpringDampingRatio },
|
||||
{ "getSpringDampingRatio", w_WheelJoint_getSpringDampingRatio },
|
||||
{ "setSpringFrequency", w_WheelJoint_setFrequency },
|
||||
{ "getSpringFrequency", w_WheelJoint_getFrequency },
|
||||
{ "setSpringDampingRatio", w_WheelJoint_setDampingRatio },
|
||||
{ "getSpringDampingRatio", w_WheelJoint_getDampingRatio },
|
||||
{ "setSpringStiffness", w_WheelJoint_setStiffness },
|
||||
{ "getSpringStiffness", w_WheelJoint_getStiffness },
|
||||
{ "setSpringDamping", w_WheelJoint_setDamping },
|
||||
{ "getSpringDamping", w_WheelJoint_getDamping },
|
||||
{ "setFrequency", w_WheelJoint_setFrequency },
|
||||
{ "getFrequency", w_WheelJoint_getFrequency },
|
||||
{ "setDampingRatio", w_WheelJoint_setDampingRatio },
|
||||
{ "getDampingRatio", w_WheelJoint_getDampingRatio },
|
||||
{ "setStiffness", w_WheelJoint_setStiffness },
|
||||
{ "getStiffness", w_WheelJoint_getStiffness },
|
||||
{ "setDamping", w_WheelJoint_setDamping },
|
||||
{ "getDamping", w_WheelJoint_getDamping },
|
||||
{ "getAxis", w_WheelJoint_getAxis },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
@@ -39,7 +39,18 @@
|
||||
#if defined(LOVE_ANDROID)
|
||||
#include "common/android.h"
|
||||
#elif defined(LOVE_LINUX)
|
||||
|
||||
#ifdef __has_include
|
||||
#if __has_include(<spawn.h>)
|
||||
#define LOVE_HAS_POSIX_SPAWN
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef LOVE_HAS_POSIX_SPAWN
|
||||
#include <spawn.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace love
|
||||
@@ -70,10 +81,12 @@ std::string System::getOS() const
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef LOVE_HAS_POSIX_SPAWN
|
||||
extern "C"
|
||||
{
|
||||
extern char **environ; // The environment, always available
|
||||
}
|
||||
#endif
|
||||
|
||||
bool System::openURL(const std::string &url) const
|
||||
{
|
||||
@@ -104,10 +117,19 @@ bool System::openURL(const std::string &url) const
|
||||
pid_t pid;
|
||||
const char *argv[] = {"xdg-open", url.c_str(), nullptr};
|
||||
|
||||
#ifdef LOVE_HAS_POSIX_SPAWN
|
||||
// Note: at the moment this process inherits our file descriptors.
|
||||
// Note: the below const_cast is really ugly as well.
|
||||
if (posix_spawnp(&pid, "xdg-open", nullptr, nullptr, const_cast<char **>(argv), environ) != 0)
|
||||
return false;
|
||||
#else
|
||||
pid = fork();
|
||||
if (pid == 0)
|
||||
{
|
||||
execvp("xdg-open", const_cast<char **>(argv));
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Check if xdg-open already completed (or failed.)
|
||||
int status = 0;
|
||||
|
||||
@@ -26,6 +26,18 @@ namespace love
|
||||
namespace window
|
||||
{
|
||||
|
||||
static bool highDPIAllowed = false;
|
||||
|
||||
void setHighDPIAllowed(bool enable)
|
||||
{
|
||||
highDPIAllowed = enable;
|
||||
}
|
||||
|
||||
bool isHighDPIAllowed()
|
||||
{
|
||||
return highDPIAllowed;
|
||||
}
|
||||
|
||||
Window::~Window()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -43,6 +43,10 @@ class Graphics;
|
||||
namespace window
|
||||
{
|
||||
|
||||
// Applied when the window is first created.
|
||||
void setHighDPIAllowed(bool enable);
|
||||
bool isHighDPIAllowed();
|
||||
|
||||
// Forward-declared so it can be used in the class methods. We can't define the
|
||||
// whole thing here because it uses the Window::Type enum.
|
||||
struct WindowSettings;
|
||||
@@ -66,7 +70,7 @@ public:
|
||||
SETTING_BORDERLESS,
|
||||
SETTING_CENTERED,
|
||||
SETTING_DISPLAY,
|
||||
SETTING_HIGHDPI,
|
||||
SETTING_HIGHDPI, // Deprecated
|
||||
SETTING_USE_DPISCALE,
|
||||
SETTING_REFRESHRATE,
|
||||
SETTING_X,
|
||||
@@ -261,7 +265,6 @@ struct WindowSettings
|
||||
bool borderless = false;
|
||||
bool centered = true;
|
||||
int display = 0;
|
||||
bool highdpi = false;
|
||||
bool usedpiscale = true;
|
||||
double refreshrate = 0.0;
|
||||
bool useposition = false;
|
||||
|
||||
@@ -94,7 +94,7 @@ void Window::setGraphics(graphics::Graphics *graphics)
|
||||
this->graphics.set(graphics);
|
||||
}
|
||||
|
||||
void Window::setGLFramebufferAttributes(int msaa, bool sRGB, bool stencil, int depth)
|
||||
void Window::setGLFramebufferAttributes(bool sRGB)
|
||||
{
|
||||
// Set GL window / framebuffer attributes.
|
||||
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
|
||||
@@ -102,12 +102,18 @@ void Window::setGLFramebufferAttributes(int msaa, bool sRGB, bool stencil, int d
|
||||
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
|
||||
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, stencil ? 8 : 0);
|
||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, depth);
|
||||
SDL_GL_SetAttribute(SDL_GL_RETAINED_BACKING, 0);
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, (msaa > 0) ? 1 : 0);
|
||||
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, (msaa > 0) ? msaa : 0);
|
||||
// Always use 24/8 depth/stencil (make sure any Graphics implementations
|
||||
// that have their own backbuffer match this, too).
|
||||
// Changing this after initial window creation would need the context to be
|
||||
// destroyed and recreated, which we really don't want.
|
||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
|
||||
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
|
||||
|
||||
// Backbuffer MSAA is handled by the love.graphics implementation.
|
||||
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
|
||||
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_FRAMEBUFFER_SRGB_CAPABLE, sRGB ? 1 : 0);
|
||||
|
||||
@@ -289,7 +295,7 @@ std::vector<Window::ContextAttribs> Window::getContextAttribsList() const
|
||||
return attribslist;
|
||||
}
|
||||
|
||||
bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowflags, graphics::Graphics::Renderer renderer, int msaa, bool stencil, int depth)
|
||||
bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowflags, graphics::Graphics::Renderer renderer)
|
||||
{
|
||||
bool needsglcontext = (windowflags & SDL_WINDOW_OPENGL) != 0;
|
||||
#ifdef LOVE_GRAPHICS_METAL
|
||||
@@ -369,10 +375,9 @@ bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowfla
|
||||
// Try each context profile in order.
|
||||
for (ContextAttribs attribs : attribslist)
|
||||
{
|
||||
int curMSAA = msaa;
|
||||
bool curSRGB = love::graphics::isGammaCorrect();
|
||||
|
||||
setGLFramebufferAttributes(curMSAA, curSRGB, stencil, depth);
|
||||
setGLFramebufferAttributes(curSRGB);
|
||||
setGLContextAttributes(attribs);
|
||||
|
||||
windowerror.clear();
|
||||
@@ -380,33 +385,14 @@ bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowfla
|
||||
|
||||
create(&attribs);
|
||||
|
||||
if (!window && curMSAA > 0)
|
||||
{
|
||||
// The MSAA setting could have caused the failure.
|
||||
setGLFramebufferAttributes(0, curSRGB, stencil, depth);
|
||||
if (create(&attribs))
|
||||
curMSAA = 0;
|
||||
}
|
||||
|
||||
if (!window && curSRGB)
|
||||
{
|
||||
// same with sRGB.
|
||||
setGLFramebufferAttributes(curMSAA, false, stencil, depth);
|
||||
// The sRGB setting could have caused the failure.
|
||||
setGLFramebufferAttributes(false);
|
||||
if (create(&attribs))
|
||||
curSRGB = false;
|
||||
}
|
||||
|
||||
if (!window && curMSAA > 0 && curSRGB)
|
||||
{
|
||||
// Or both!
|
||||
setGLFramebufferAttributes(0, false, stencil, depth);
|
||||
if (create(&attribs))
|
||||
{
|
||||
curMSAA = 0;
|
||||
curSRGB = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (window && glcontext)
|
||||
{
|
||||
// Store the successful context attributes so we can re-use them in
|
||||
@@ -481,6 +467,9 @@ bool Window::setWindow(int width, int height, WindowSettings *settings)
|
||||
|
||||
auto renderer = graphics != nullptr ? graphics->getRenderer() : graphics::Graphics::RENDERER_NONE;
|
||||
|
||||
if (isOpen())
|
||||
updateSettings(this->settings, false);
|
||||
|
||||
WindowSettings f;
|
||||
|
||||
if (settings)
|
||||
@@ -500,54 +489,11 @@ bool Window::setWindow(int width, int height, WindowSettings *settings)
|
||||
height = mode.h;
|
||||
}
|
||||
|
||||
Uint32 sdlflags = 0;
|
||||
|
||||
if (renderer == graphics::Graphics::RENDERER_OPENGL)
|
||||
sdlflags |= SDL_WINDOW_OPENGL;
|
||||
|
||||
#ifdef LOVE_GRAPHICS_METAL
|
||||
if (renderer == graphics::Graphics::RENDERER_METAL)
|
||||
sdlflags |= SDL_WINDOW_METAL;
|
||||
#endif
|
||||
|
||||
// On Android we always must have fullscreen type FULLSCREEN_TYPE_DESKTOP
|
||||
#ifdef LOVE_ANDROID
|
||||
f.fstype = FULLSCREEN_DESKTOP;
|
||||
#endif
|
||||
|
||||
if (f.fullscreen)
|
||||
{
|
||||
if (f.fstype == FULLSCREEN_DESKTOP)
|
||||
sdlflags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||
else
|
||||
{
|
||||
sdlflags |= SDL_WINDOW_FULLSCREEN;
|
||||
SDL_DisplayMode mode = {0, width, height, 0, nullptr};
|
||||
|
||||
// Fullscreen window creation will bug out if no mode can be used.
|
||||
if (SDL_GetClosestDisplayMode(f.display, &mode, &mode) == nullptr)
|
||||
{
|
||||
// GetClosestDisplayMode will fail if we request a size larger
|
||||
// than the largest available display mode, so we'll try to use
|
||||
// the largest (first) mode in that case.
|
||||
if (SDL_GetDisplayMode(f.display, 0, &mode) < 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
width = mode.w;
|
||||
height = mode.h;
|
||||
}
|
||||
}
|
||||
|
||||
if (f.resizable)
|
||||
sdlflags |= SDL_WINDOW_RESIZABLE;
|
||||
|
||||
if (f.borderless)
|
||||
sdlflags |= SDL_WINDOW_BORDERLESS;
|
||||
|
||||
if (f.highdpi)
|
||||
sdlflags |= SDL_WINDOW_ALLOW_HIGHDPI;
|
||||
|
||||
int x = f.x;
|
||||
int y = f.y;
|
||||
|
||||
@@ -567,10 +513,81 @@ bool Window::setWindow(int width, int height, WindowSettings *settings)
|
||||
x = y = SDL_WINDOWPOS_UNDEFINED_DISPLAY(f.display);
|
||||
}
|
||||
|
||||
close();
|
||||
SDL_DisplayMode fsmode = {0, width, height, 0, nullptr};
|
||||
|
||||
if (!createWindowAndContext(x, y, width, height, sdlflags, renderer, f.msaa, f.stencil, f.depth))
|
||||
return false;
|
||||
if (f.fullscreen && f.fstype == FULLSCREEN_EXCLUSIVE)
|
||||
{
|
||||
// Fullscreen window creation will bug out if no mode can be used.
|
||||
if (SDL_GetClosestDisplayMode(f.display, &fsmode, &fsmode) == nullptr)
|
||||
{
|
||||
// GetClosestDisplayMode will fail if we request a size larger
|
||||
// than the largest available display mode, so we'll try to use
|
||||
// the largest (first) mode in that case.
|
||||
if (SDL_GetDisplayMode(f.display, 0, &fsmode) < 0)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool needsetmode = false;
|
||||
|
||||
Uint32 sdlflags = 0;
|
||||
|
||||
if (f.fullscreen)
|
||||
{
|
||||
if (f.fstype == FULLSCREEN_DESKTOP)
|
||||
sdlflags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||
else
|
||||
{
|
||||
sdlflags |= SDL_WINDOW_FULLSCREEN;
|
||||
width = fsmode.w;
|
||||
height = fsmode.h;
|
||||
}
|
||||
}
|
||||
|
||||
if (renderer == graphics::Graphics::RENDERER_OPENGL)
|
||||
sdlflags |= SDL_WINDOW_OPENGL;
|
||||
|
||||
#ifdef LOVE_GRAPHICS_METAL
|
||||
if (renderer == graphics::Graphics::RENDERER_METAL)
|
||||
sdlflags |= SDL_WINDOW_METAL;
|
||||
#endif
|
||||
|
||||
if (isOpen())
|
||||
{
|
||||
if (SDL_SetWindowFullscreen(window, sdlflags) == 0 && renderer == graphics::Graphics::RENDERER_OPENGL)
|
||||
SDL_GL_MakeCurrent(window, glcontext);
|
||||
|
||||
if (!f.fullscreen)
|
||||
SDL_SetWindowSize(window, width, height);
|
||||
|
||||
// On linux systems 2.0.5+ might not be available...
|
||||
// TODO: require at least 2.0.5?
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 5)
|
||||
if (this->settings.resizable != f.resizable)
|
||||
SDL_SetWindowResizable(window, f.resizable ? SDL_TRUE : SDL_FALSE);
|
||||
#endif
|
||||
|
||||
if (this->settings.borderless != f.borderless)
|
||||
SDL_SetWindowBordered(window, f.borderless ? SDL_FALSE : SDL_TRUE);
|
||||
}
|
||||
else
|
||||
{
|
||||
sdlflags |= SDL_WINDOW_OPENGL;
|
||||
|
||||
if (f.resizable)
|
||||
sdlflags |= SDL_WINDOW_RESIZABLE;
|
||||
|
||||
if (f.borderless)
|
||||
sdlflags |= SDL_WINDOW_BORDERLESS;
|
||||
|
||||
if (isHighDPIAllowed())
|
||||
sdlflags |= SDL_WINDOW_ALLOW_HIGHDPI;
|
||||
|
||||
if (!createWindowAndContext(x, y, width, height, sdlflags, renderer))
|
||||
return false;
|
||||
|
||||
needsetmode = true;
|
||||
}
|
||||
|
||||
// Make sure the window keeps any previously set icon.
|
||||
setIcon(icon.get());
|
||||
@@ -581,7 +598,7 @@ bool Window::setWindow(int width, int height, WindowSettings *settings)
|
||||
// Enforce minimum window dimensions.
|
||||
SDL_SetWindowMinimumSize(window, f.minwidth, f.minheight);
|
||||
|
||||
if ((f.useposition || f.centered) && !f.fullscreen)
|
||||
if (this->settings.display != f.display || ((f.useposition || f.centered) && !f.fullscreen))
|
||||
SDL_SetWindowPosition(window, x, y);
|
||||
|
||||
SDL_RaiseWindow(window);
|
||||
@@ -595,15 +612,23 @@ bool Window::setWindow(int width, int height, WindowSettings *settings)
|
||||
double scaledw, scaledh;
|
||||
fromPixels((double) pixelWidth, (double) pixelHeight, scaledw, scaledh);
|
||||
|
||||
void *context = nullptr;
|
||||
if (renderer == graphics::Graphics::RENDERER_OPENGL)
|
||||
context = (void *) glcontext;
|
||||
if (needsetmode)
|
||||
{
|
||||
void *context = nullptr;
|
||||
if (renderer == graphics::Graphics::RENDERER_OPENGL)
|
||||
context = (void *) glcontext;
|
||||
#ifdef LOVE_GRAPHICS_METAL
|
||||
if (renderer == graphics::Graphics::RENDERER_METAL && metalView)
|
||||
context = (void *) SDL_Metal_GetLayer(metalView);
|
||||
if (renderer == graphics::Graphics::RENDERER_METAL && metalView)
|
||||
context = (void *) SDL_Metal_GetLayer(metalView);
|
||||
#endif
|
||||
|
||||
graphics->setMode(context, (int) scaledw, (int) scaledh, pixelWidth, pixelHeight, f.stencil, f.depth);
|
||||
graphics->setMode(context, (int) scaledw, (int) scaledh, pixelWidth, pixelHeight, f.stencil, f.msaa);
|
||||
this->settings.msaa = graphics->getBackbufferMSAA();
|
||||
}
|
||||
else
|
||||
{
|
||||
graphics->setViewportSize((int) scaledw, (int) scaledh, pixelWidth, pixelHeight);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef LOVE_ANDROID
|
||||
@@ -680,7 +705,8 @@ void Window::updateSettings(const WindowSettings &newsettings, bool updateGraphi
|
||||
|
||||
getPosition(settings.x, settings.y, settings.display);
|
||||
|
||||
settings.highdpi = (wflags & SDL_WINDOW_ALLOW_HIGHDPI) != 0;
|
||||
setHighDPIAllowed((wflags & SDL_WINDOW_ALLOW_HIGHDPI) != 0);
|
||||
|
||||
settings.usedpiscale = newsettings.usedpiscale;
|
||||
|
||||
// Only minimize on focus loss if the window is in exclusive-fullscreen mode
|
||||
@@ -689,17 +715,6 @@ void Window::updateSettings(const WindowSettings &newsettings, bool updateGraphi
|
||||
else
|
||||
SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0");
|
||||
|
||||
// Verify MSAA setting.
|
||||
int buffers = 0;
|
||||
int samples = 0;
|
||||
|
||||
if ((wflags & SDL_WINDOW_OPENGL) != 0)
|
||||
{
|
||||
SDL_GL_GetAttribute(SDL_GL_MULTISAMPLEBUFFERS, &buffers);
|
||||
SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &samples);
|
||||
}
|
||||
|
||||
settings.msaa = (buffers > 0 ? samples : 0);
|
||||
settings.vsync = getVSync();
|
||||
|
||||
settings.stencil = newsettings.stencil;
|
||||
@@ -773,7 +788,7 @@ void Window::close(bool allowExceptions)
|
||||
open = false;
|
||||
}
|
||||
|
||||
bool Window::setFullscreen(bool fullscreen, Window::FullscreenType fstype)
|
||||
bool Window::setFullscreen(bool fullscreen, FullscreenType fstype)
|
||||
{
|
||||
if (!window)
|
||||
return false;
|
||||
@@ -812,9 +827,10 @@ bool Window::setFullscreen(bool fullscreen, Window::FullscreenType fstype)
|
||||
{
|
||||
if (glcontext)
|
||||
SDL_GL_MakeCurrent(window, glcontext);
|
||||
|
||||
updateSettings(newsettings, true);
|
||||
|
||||
// Apparently this gets un-set when we exit fullscreen (at least in macOS).
|
||||
// This gets un-set when we exit fullscreen (at least in macOS).
|
||||
if (!fullscreen)
|
||||
SDL_SetWindowMinimumSize(window, settings.minwidth, settings.minheight);
|
||||
|
||||
|
||||
@@ -131,8 +131,6 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
void close(bool allowExceptions);
|
||||
|
||||
struct ContextAttribs
|
||||
{
|
||||
int versionMajor;
|
||||
@@ -141,11 +139,13 @@ private:
|
||||
bool debug;
|
||||
};
|
||||
|
||||
void setGLFramebufferAttributes(int msaa, bool sRGB, bool stencil, int depth);
|
||||
void close(bool allowExceptions);
|
||||
|
||||
void setGLFramebufferAttributes(bool sRGB);
|
||||
void setGLContextAttributes(const ContextAttribs &attribs);
|
||||
bool checkGLVersion(const ContextAttribs &attribs, std::string &outversion);
|
||||
std::vector<ContextAttribs> getContextAttribsList() const;
|
||||
bool createWindowAndContext(int x, int y, int w, int h, Uint32 windowflags, graphics::Graphics::Renderer renderer, int msaa, bool stencil, int depth);
|
||||
bool createWindowAndContext(int x, int y, int w, int h, Uint32 windowflags, graphics::Graphics::Renderer renderer);
|
||||
|
||||
// Update the saved window settings based on the window's actual state.
|
||||
void updateSettings(const WindowSettings &newsettings, bool updateGraphicsViewport);
|
||||
|
||||
@@ -75,9 +75,19 @@ static int readWindowSettings(lua_State *L, int idx, WindowSettings &settings)
|
||||
settings.borderless = luax_boolflag(L, idx, settingName(Window::SETTING_BORDERLESS), settings.borderless);
|
||||
settings.centered = luax_boolflag(L, idx, settingName(Window::SETTING_CENTERED), settings.centered);
|
||||
settings.display = luax_intflag(L, idx, settingName(Window::SETTING_DISPLAY), settings.display+1) - 1;
|
||||
settings.highdpi = luax_boolflag(L, idx, settingName(Window::SETTING_HIGHDPI), settings.highdpi);
|
||||
settings.usedpiscale = luax_boolflag(L, idx, settingName(Window::SETTING_USE_DPISCALE), settings.usedpiscale);
|
||||
|
||||
lua_getfield(L, idx, settingName(Window::SETTING_HIGHDPI));
|
||||
if (!lua_isnoneornil(L, -1))
|
||||
{
|
||||
luax_markdeprecated(L, "window.highdpi", API_FIELD, DEPRECATED_REPLACED, "t.highdpi in love.conf");
|
||||
bool highdpi = luax_checkboolean(L, -1);
|
||||
if (!instance()->isOpen())
|
||||
setHighDPIAllowed(highdpi);
|
||||
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, idx, settingName(Window::SETTING_VSYNC));
|
||||
if (lua_isnumber(L, -1))
|
||||
settings.vsync = (int) lua_tointeger(L, -1);
|
||||
@@ -201,9 +211,6 @@ int w_getMode(lua_State *L)
|
||||
lua_pushinteger(L, settings.display + 1);
|
||||
lua_setfield(L, -2, settingName(Window::SETTING_DISPLAY));
|
||||
|
||||
luax_pushboolean(L, settings.highdpi);
|
||||
lua_setfield(L, -2, settingName(Window::SETTING_HIGHDPI));
|
||||
|
||||
luax_pushboolean(L, settings.usedpiscale);
|
||||
lua_setfield(L, -2, settingName(Window::SETTING_USE_DPISCALE));
|
||||
|
||||
@@ -219,6 +226,12 @@ int w_getMode(lua_State *L)
|
||||
return 3;
|
||||
}
|
||||
|
||||
int w_isHighDPIAllowed(lua_State *L)
|
||||
{
|
||||
luax_pushboolean(L, isHighDPIAllowed());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_getDisplayOrientation(lua_State *L)
|
||||
{
|
||||
int displayindex = 0;
|
||||
@@ -618,6 +631,7 @@ static const luaL_Reg functions[] =
|
||||
{ "setMode", w_setMode },
|
||||
{ "updateMode", w_updateMode },
|
||||
{ "getMode", w_getMode },
|
||||
{ "isHighDPIAllowed", w_isHighDPIAllowed },
|
||||
{ "getDisplayOrientation", w_getDisplayOrientation },
|
||||
{ "getFullscreenModes", w_getFullscreenModes },
|
||||
{ "setFullscreen", w_setFullscreen },
|
||||
|
||||
Reference in New Issue
Block a user