Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • gamedev/fggl
  • onuralpsezer/fggl
2 results
Show changes
Showing
with 8920 additions and 1022 deletions
......@@ -20,44 +20,44 @@
namespace fggl::gui {
void buttonBorder( gfx::Path2D& path, glm::vec2 pos, glm::vec2 size ) {
void button_border(gfx::Path2D &path, glm::vec2 pos, glm::vec2 size) {
// outer box
path.colour( {1.0f, 0.0f, 0.0f} );
path.pathTo( { pos.x + size.x, pos.y } );
path.pathTo( { pos.x + size.x, pos.y + size.y } );
path.pathTo( { pos.x, pos.y + size.y } );
path.colour({1.0F, 0.0F, 0.0F});
path.pathTo({pos.x + size.x, pos.y});
path.pathTo({pos.x + size.x, pos.y + size.y});
path.pathTo({pos.x, pos.y + size.y});
path.close();
// inner box
math::vec2 innerTop { pos.x + 5, pos.y + 5 };
math::vec2 innerBottom { pos.x + size.x - 5, pos.y + size.y - 5 };
path.colour( {1.0f, 1.0f, 0.0f} );
path.moveTo( { innerTop.x, innerTop.y } );
path.pathTo( { innerBottom.x, innerTop.y } );
path.pathTo( { innerBottom.x, innerBottom.y } );
path.pathTo( { innerTop.x, innerBottom.y } );
path.pathTo( { innerTop.x, innerTop.y } );
math::vec2 innerTop{pos.x + 5, pos.y + 5};
math::vec2 innerBottom{pos.x + size.x - 5, pos.y + size.y - 5};
path.colour({1.0F, 1.0F, 0.0F});
path.moveTo({innerTop.x, innerTop.y});
path.pathTo({innerBottom.x, innerTop.y});
path.pathTo({innerBottom.x, innerBottom.y});
path.pathTo({innerTop.x, innerBottom.y});
path.pathTo({innerTop.x, innerTop.y});
}
void draw_box( gfx::Path2D& path, glm::vec2 topLeft, glm::vec2 bottomRight ) {
path.moveTo( { topLeft.x, topLeft.y } );
path.pathTo( { bottomRight.x, topLeft.y } );
path.pathTo( { bottomRight.x, bottomRight.y } );
path.pathTo( { topLeft.x, bottomRight.y } );
path.pathTo( { topLeft.x, topLeft.y } );
void draw_box(gfx::Path2D &path, glm::vec2 topLeft, glm::vec2 bottomRight) {
path.moveTo({topLeft.x, topLeft.y});
path.pathTo({bottomRight.x, topLeft.y});
path.pathTo({bottomRight.x, bottomRight.y});
path.pathTo({topLeft.x, bottomRight.y});
path.pathTo({topLeft.x, topLeft.y});
}
void draw_progress( gfx::Path2D& path, glm::vec2 topLeft, glm::vec2 size, float value ) {
const auto bottomRight { topLeft + size };
void draw_progress(gfx::Path2D &path, glm::vec2 topLeft, glm::vec2 size, float value) {
const auto bottomRight{topLeft + size};
// background
path.colour( {0.5f, 0.5f, 0.5f} );
draw_box( path, topLeft, bottomRight );
path.colour({0.5F, 0.5F, 0.5F});
draw_box(path, topLeft, bottomRight);
// fill
math::vec2 innerTop { topLeft.x + 5, topLeft.y + 5 };
math::vec2 innerBottom { bottomRight.x - 5, bottomRight.y - 5 };
math::vec2 innerTop{topLeft.x + 5, topLeft.y + 5};
math::vec2 innerBottom{bottomRight.x - 5, bottomRight.y - 5};
// figure out how wide the bar should be
float barWidth = (innerBottom.x - innerTop.x) * value;
......@@ -65,55 +65,55 @@ namespace fggl::gui {
innerBottom.x = innerTop.x + barWidth;
// draw the bar
path.colour( {0.8f, 0.0f, 0.0f} );
draw_box( path, innerTop, innerBottom );
path.colour({0.8F, 0.0F, 0.0F});
draw_box(path, innerTop, innerBottom);
// part of the bar that's not filled in
math::vec2 emptyTop { innerBottom.x, innerTop.y };
math::vec2 emptyBottom { trueBottom, innerBottom.y };
path.colour( {0.4f, 0.0f, 0.0f} );
draw_box( path, emptyTop, emptyBottom );
math::vec2 emptyTop{innerBottom.x, innerTop.y};
math::vec2 emptyBottom{trueBottom, innerBottom.y};
path.colour({0.4F, 0.0F, 0.0F});
draw_box(path, emptyTop, emptyBottom);
}
void draw_slider( gfx::Path2D& path, glm::vec2 topLeft, glm::vec2 size, float value ) {
draw_progress( path, topLeft, size, value );
void draw_slider(gfx::Path2D &path, glm::vec2 topLeft, glm::vec2 size, float value) {
draw_progress(path, topLeft, size, value);
// dimensions
const auto bottomRight { topLeft + size };
const math::vec2 innerTop { topLeft.x + 5, topLeft.y + 5 };
const math::vec2 innerBottom { bottomRight.x - 5, bottomRight.y - 5 };
const auto bottomRight{topLeft + size};
const math::vec2 innerTop{topLeft.x + 5, topLeft.y + 5};
const math::vec2 innerBottom{bottomRight.x - 5, bottomRight.y - 5};
// selector bar
float trackWidth = innerBottom.x - innerTop.x;
float selectorValue = trackWidth * value;
float selectorWidth = 6;
math::vec2 selectorTop { innerTop.x + selectorValue - ( selectorWidth/2), topLeft.y };
math::vec2 selectorBottom { selectorTop.x + selectorWidth, bottomRight.y };
path.colour( {1.0f, 1.0f, 1.0f} );
draw_box( path, selectorTop, selectorBottom );
math::vec2 selectorTop{innerTop.x + selectorValue - (selectorWidth / 2), topLeft.y};
math::vec2 selectorBottom{selectorTop.x + selectorWidth, bottomRight.y};
path.colour({1.0F, 1.0F, 1.0F});
draw_box(path, selectorTop, selectorBottom);
}
void draw_button( gfx::Path2D& path, glm::vec2 pos, glm::vec2 size, bool active, bool pressed) {
void draw_button(gfx::Path2D &path, glm::vec2 pos, glm::vec2 size, bool active, bool pressed) {
// locations
math::vec2 outerTop { pos };
math::vec2 outerBottom { pos + size };
math::vec2 innerTop { pos.x + 5, pos.y + 5 };
math::vec2 innerBottom { pos.x + size.x - 5, pos.y + size.y - 5 };
math::vec2 outerTop{pos};
math::vec2 outerBottom{pos + size};
math::vec2 innerTop{pos.x + 5, pos.y + 5};
math::vec2 innerBottom{pos.x + size.x - 5, pos.y + size.y - 5};
math::vec3 baseColour{ 0.5f, 0.5f, 0.5f };
math::vec3 baseColour{0.5F, 0.5F, 0.5F};
if ( active ) {
baseColour *= 1.2f;
if (active) {
baseColour *= 1.2F;
}
if ( pressed ) {
baseColour *= 0.8f;
if (pressed) {
baseColour *= 0.8F;
}
math::vec3 lightColour{ baseColour * 1.2f };
math::vec3 darkColour{ baseColour * 0.8f };
if ( pressed ) {
math::vec3 lightColour{baseColour * 1.2F};
math::vec3 darkColour{baseColour * 0.8F};
if (pressed) {
// flip light and dark for selected buttons
auto tmp = darkColour;
darkColour = lightColour;
......@@ -121,39 +121,39 @@ namespace fggl::gui {
}
// bottom side
path.colour( lightColour );
path.moveTo( outerTop );
path.pathTo( innerTop );
path.pathTo( { innerBottom.x, innerTop.y } );
path.pathTo( { outerBottom.x, outerTop.y } );
path.pathTo( outerTop );
path.colour(lightColour);
path.moveTo(outerTop);
path.pathTo(innerTop);
path.pathTo({innerBottom.x, innerTop.y});
path.pathTo({outerBottom.x, outerTop.y});
path.pathTo(outerTop);
// left side
path.colour( lightColour );
path.moveTo( outerTop );
path.pathTo( innerTop );
path.pathTo( { innerTop.x, innerBottom.y } );
path.pathTo( { outerTop.x, outerBottom.y } );
path.pathTo( outerTop );
path.colour(lightColour);
path.moveTo(outerTop);
path.pathTo(innerTop);
path.pathTo({innerTop.x, innerBottom.y});
path.pathTo({outerTop.x, outerBottom.y});
path.pathTo(outerTop);
// top side
path.colour( darkColour );
path.moveTo( { outerTop.x, outerBottom.y} );
path.pathTo( { innerTop.x, innerBottom.y} );
path.pathTo( innerBottom );
path.pathTo( outerBottom );
path.pathTo( { outerTop.x, outerBottom.y} );
path.colour(darkColour);
path.moveTo({outerTop.x, outerBottom.y});
path.pathTo({innerTop.x, innerBottom.y});
path.pathTo(innerBottom);
path.pathTo(outerBottom);
path.pathTo({outerTop.x, outerBottom.y});
// right side
path.colour( darkColour );
path.moveTo( outerBottom );
path.pathTo( innerBottom );
path.pathTo( { innerBottom.x, innerTop.y } );
path.pathTo( { outerBottom.x, outerTop.y } );
path.pathTo( outerBottom );
path.colour(darkColour);
path.moveTo(outerBottom);
path.pathTo(innerBottom);
path.pathTo({innerBottom.x, innerTop.y});
path.pathTo({outerBottom.x, outerTop.y});
path.pathTo(outerBottom);
// inner box
path.colour( baseColour );
draw_box( path, innerTop, innerBottom );
path.colour(baseColour);
draw_box(path, innerTop, innerBottom);
}
}
\ No newline at end of file
......@@ -23,10 +23,10 @@
namespace fggl::gui {
Button::Button( math::vec2 pos, math::vec2 size) : Widget(pos, size), m_label(pos, size), m_hover(false), m_active(false) {}
Button::Button(math::vec2 pos, math::vec2 size) : Widget(pos, size), m_label(pos, size), m_active(false) {}
void Button::render(gfx::Paint &paint) {
gfx::Path2D path{ topLeft() };
gfx::Path2D path{topLeft()};
draw_button(path, topLeft(), size(), m_hover, m_active);
paint.fill(path);
......@@ -35,41 +35,37 @@ namespace fggl::gui {
void Button::activate() {
m_active = !m_active;
if ( m_active ) {
for( auto& callback : m_callbacks ) {
if (m_active) {
for (auto &callback : m_callbacks) {
callback();
m_active = false;
}
m_active = false;
}
}
void Button::onEnter() {
m_hover = true;
}
void Button::onExit() {
m_hover = false;
}
void Button::addCallback(Callback cb) {
m_callbacks.push_back( cb );
m_callbacks.push_back(cb);
}
void Button::label(const std::string &value) {
m_label.text(value);
}
std::string Button::label() const {
void Button::layout() {
m_label.size(topLeft(), size());
}
auto Button::label() const -> std::string {
return m_label.text();
}
void Label::layout() {
if ( m_font == nullptr ) {
if (m_font == nullptr) {
return;
}
math::vec2 size;
for (const auto& letter : m_value) {
for (const auto &letter : m_value) {
auto metrics = m_font->metrics(letter);
size.x += (metrics.advance << 6);
size.y = std::max(size.y, metrics.size.y);
......
......@@ -23,156 +23,160 @@
namespace fggl::input {
void process_arcball(entity::EntityManager &ecs, const Input &input, entity::EntityID cam) {
// see https://asliceofrendering.com/camera/2019/11/30/ArcballCamera/
auto& camTransform = ecs.get<fggl::math::Transform>(cam);
auto& camComp = ecs.get<fggl::gfx::Camera>(cam);
auto &mouse = input.mouse;
glm::vec4 position(camTransform.origin(), 1.0f);
glm::vec4 pivot(camComp.target, 1.0f);
glm::mat4 view = glm::lookAt(camTransform.origin(), camComp.target, camTransform.up());
glm::vec3 viewDir = -glm::transpose(view)[2];
glm::vec3 rightDir = glm::transpose(view)[0];
float deltaAngleX = (2 * M_PI);
float deltaAngleY = (M_PI);
float xAngle = (-mouse.axisDelta(fggl::input::MouseAxis::X)) * deltaAngleX;
float yAngle = (-mouse.axisDelta(fggl::input::MouseAxis::Y)) * deltaAngleY;
// rotate the camera around the pivot on the first axis
glm::mat4x4 rotationMatrixX(1.0f);
rotationMatrixX = glm::rotate(rotationMatrixX, xAngle, fggl::math::UP);
position = (rotationMatrixX * (position - pivot)) + pivot;
// rotate the camera aroud the pivot on the second axis
glm::mat4x4 rotationMatrixY(1.0f);
rotationMatrixY = glm::rotate(rotationMatrixY, yAngle, rightDir);
glm::vec3 finalPos = (rotationMatrixY * (position - pivot)) + pivot;
camTransform.origin(finalPos);
}
void process_scroll(entity::EntityManager &ecs, const Input &input, entity::EntityID cam, float minZoom, float maxZoom){
auto& camTransform = ecs.get<fggl::math::Transform>(cam);
auto& camComp = ecs.get<fggl::gfx::Camera>(cam);
const glm::vec3 dir = ( camTransform.origin() - camComp.target );
const glm::vec3 forward = glm::normalize( dir );
void process_arcball(entity::EntityManager &ecs, const Input &input, entity::EntityID cam) {
// see https://asliceofrendering.com/camera/2019/11/30/ArcballCamera/
auto &camTransform = ecs.get<fggl::math::Transform>(cam);
auto &camComp = ecs.get<fggl::gfx::Camera>(cam);
const auto &mouse = input.mouse;
glm::vec4 position(camTransform.origin(), 1.0F);
glm::vec4 pivot(camComp.target, 1.0F);
glm::mat4 view = glm::lookAt(camTransform.origin(), camComp.target, camTransform.up());
glm::vec3 viewDir = -glm::transpose(view)[2];
glm::vec3 rightDir = glm::transpose(view)[0];
float deltaAngleX = (2 * M_PI);
float deltaAngleY = (M_PI);
float xAngle = (-mouse.axisDelta(fggl::input::MouseAxis::X)) * deltaAngleX;
float yAngle = (-mouse.axisDelta(fggl::input::MouseAxis::Y)) * deltaAngleY;
// rotate the camera around the pivot on the first axis
glm::mat4x4 rotationMatrixX(1.0F);
rotationMatrixX = glm::rotate(rotationMatrixX, xAngle, fggl::math::UP);
position = (rotationMatrixX * (position - pivot)) + pivot;
// rotate the camera around the pivot on the second axis
glm::mat4x4 rotationMatrixY(1.0F);
rotationMatrixY = glm::rotate(rotationMatrixY, yAngle, rightDir);
glm::vec3 finalPos = (rotationMatrixY * (position - pivot)) + pivot;
camTransform.origin(finalPos);
}
void process_scroll(entity::EntityManager &ecs,
const Input &input,
entity::EntityID cam,
float minZoom,
float maxZoom) {
auto &camTransform = ecs.get<fggl::math::Transform>(cam);
auto &camComp = ecs.get<fggl::gfx::Camera>(cam);
const glm::vec3 dir = (camTransform.origin() - camComp.target);
const glm::vec3 forward = glm::normalize(dir);
glm::vec3 motion(0.0F);
float delta = input.mouse.axis( fggl::input::MouseAxis::SCROLL_Y );
if ( (glm::length( dir ) < maxZoom && delta < 0.0f) || (glm::length( dir ) > minZoom && delta > 0.0f) ) {
float delta = input.mouse.axis(fggl::input::MouseAxis::SCROLL_Y);
if ((glm::length(dir) < maxZoom && delta < 0.0F) || (glm::length(dir) > minZoom && delta > 0.0F)) {
motion -= (forward * delta);
camTransform.origin(camTransform.origin() + motion);
}
}
void process_freecam(entity::EntityManager &ecs, const Input &input, entity::EntityID cam) {
float rotationValue = 0.0f;
glm::vec3 translation(0.0f);
void process_freecam(entity::EntityManager &ecs, const Input &input, entity::EntityID cam) {
float rotationValue = 0.0F;
glm::vec3 translation(0.0F);
const auto &keyboard = input.keyboard;
auto &settings = ecs.get<FreeCamKeys>(cam);
// calculate rotation (user input)
if (keyboard.down(settings.rotate_cw)) {
rotationValue = ROT_SPEED;
} else if (keyboard.down(settings.rotate_ccw)) {
rotationValue = -ROT_SPEED;
}
auto &keyboard = input.keyboard;
auto& settings = ecs.get<FreeCamKeys>(cam);
// calculate rotation (user input)
if (keyboard.down(settings.rotate_cw)) {
rotationValue = ROT_SPEED;
} else if (keyboard.down(settings.rotate_ccw)) {
rotationValue = -ROT_SPEED;
}
// calculate movement (user input)
if (keyboard.down(settings.forward)) {
translation -= fggl::math::RIGHT;
}
if (keyboard.down(settings.backward)) {
translation += fggl::math::RIGHT;
}
if (keyboard.down(settings.right)) {
translation += fggl::math::FORWARD;
}
if (keyboard.down(settings.left)) {
translation -= fggl::math::FORWARD;
}
// apply rotation/movement
auto camTransform = ecs.get<fggl::math::Transform>(cam);
auto camComp = ecs.get<fggl::gfx::Camera>(cam);
glm::vec4 position(camTransform.origin(), 1.0f);
glm::vec4 pivot(camComp.target, 1.0f);
// apply movement
if (translation != glm::vec3(0.0f)) {
const auto rotation = (position - pivot);
const float angle = atan2f(rotation.x, rotation.z);
const auto rotationMat = glm::rotate(MAT_IDENTITY, angle, fggl::math::UP);
auto deltaMove = (rotationMat * glm::vec4(translation, 1.0f)) * PAN_SPEED;
deltaMove.w = 0.0f;
position += deltaMove;
pivot += deltaMove;
}
// apply rotation
if (rotationValue != 0.0f) {
glm::mat4 rotation = glm::rotate(MAT_IDENTITY, rotationValue, fggl::math::UP);
position = (rotation * (position - pivot)) + pivot;
}
camTransform.origin(position);
camComp.target = pivot;
}
void process_edgescroll(entity::EntityManager &ecs, const Input &input, entity::EntityID cam) {
glm::vec3 translation(0.0f);
auto &mouse = input.mouse;
// calculate movement (user input)
if (mouse.axis(MouseAxis::Y) < 0.9f) {
translation -= fggl::math::RIGHT;
}
if (mouse.axis(MouseAxis::Y) > -0.9f) {
translation += fggl::math::RIGHT;
}
if (mouse.axis(MouseAxis::X) > -0.9f) {
translation += fggl::math::FORWARD;
}
if (mouse.axis(MouseAxis::X) < 0.9f) {
translation -= fggl::math::FORWARD;
}
// apply rotation/movement
auto& camTransform = ecs.get<fggl::math::Transform>(cam);
auto& camComp = ecs.get<fggl::gfx::Camera>(cam);
glm::vec4 position(camTransform.origin(), 1.0f);
glm::vec4 pivot(camComp.target, 1.0f);
// apply movement
if (translation != glm::vec3(0.0f)) {
const auto rotation = (position - pivot);
const float angle = atan2f(rotation.x, rotation.z);
const auto rotationMat = glm::rotate(MAT_IDENTITY, angle, fggl::math::UP);
auto deltaMove = (rotationMat * glm::vec4(translation, 1.0f)) * PAN_SPEED;
deltaMove.w = 0.0f;
position += deltaMove;
pivot += deltaMove;
}
// move camera
camTransform.origin(position);
camComp.target = pivot;
}
// calculate movement (user input)
if (keyboard.down(settings.forward)) {
translation -= fggl::math::RIGHT;
}
if (keyboard.down(settings.backward)) {
translation += fggl::math::RIGHT;
}
if (keyboard.down(settings.right)) {
translation += fggl::math::FORWARD;
}
if (keyboard.down(settings.left)) {
translation -= fggl::math::FORWARD;
}
// apply rotation/movement
auto camTransform = ecs.get<fggl::math::Transform>(cam);
auto camComp = ecs.get<fggl::gfx::Camera>(cam);
glm::vec4 position(camTransform.origin(), 1.0F);
glm::vec4 pivot(camComp.target, 1.0F);
// apply movement
if (translation != glm::vec3(0.0F)) {
const auto rotation = (position - pivot);
const float angle = atan2f(rotation.x, rotation.z);
const auto rotationMat = glm::rotate(MAT_IDENTITY, angle, fggl::math::UP);
auto deltaMove = (rotationMat * glm::vec4(translation, 1.0F)) * PAN_SPEED;
deltaMove.w = 0.0F;
position += deltaMove;
pivot += deltaMove;
}
// apply rotation
if (rotationValue != 0.0F) {
glm::mat4 rotation = glm::rotate(MAT_IDENTITY, rotationValue, fggl::math::UP);
position = (rotation * (position - pivot)) + pivot;
}
camTransform.origin(position);
camComp.target = pivot;
}
void process_edgescroll(entity::EntityManager &ecs, const Input &input, entity::EntityID cam) {
glm::vec3 translation(0.0F);
const auto &mouse = input.mouse;
// calculate movement (user input)
if (mouse.axis(MouseAxis::Y) < 0.9F) {
translation -= fggl::math::RIGHT;
}
if (mouse.axis(MouseAxis::Y) > -0.9F) {
translation += fggl::math::RIGHT;
}
if (mouse.axis(MouseAxis::X) > -0.9F) {
translation += fggl::math::FORWARD;
}
if (mouse.axis(MouseAxis::X) < 0.9F) {
translation -= fggl::math::FORWARD;
}
// apply rotation/movement
auto &camTransform = ecs.get<fggl::math::Transform>(cam);
auto &camComp = ecs.get<fggl::gfx::Camera>(cam);
glm::vec4 position(camTransform.origin(), 1.0F);
glm::vec4 pivot(camComp.target, 1.0F);
// apply movement
if (translation != glm::vec3(0.0F)) {
const auto rotation = (position - pivot);
const float angle = atan2f(rotation.x, rotation.z);
const auto rotationMat = glm::rotate(MAT_IDENTITY, angle, fggl::math::UP);
auto deltaMove = (rotationMat * glm::vec4(translation, 1.0F)) * PAN_SPEED;
deltaMove.w = 0.0F;
position += deltaMove;
pivot += deltaMove;
}
// move camera
camTransform.origin(position);
camComp.target = pivot;
}
}
......@@ -16,12 +16,11 @@
namespace fggl::input {
void MouseState::operator=(const MouseState& rhs) {
for ( int i=0; i<4; i++ ) {
axis[i] = rhs.axis[i];
}
buttons = rhs.buttons;
}
void MouseState::operator=(const MouseState &rhs) {
for (int i = 0; i < 4; i++) {
axis[i] = rhs.axis[i];
}
buttons = rhs.buttons;
}
} // namespace fggl::input
# math
find_package( glm CONFIG REQUIRED )
target_link_libraries( fggl PUBLIC glm::glm )
find_package(glm CONFIG REQUIRED)
target_link_libraries(fggl PUBLIC glm::glm)
target_sources(fggl
PRIVATE
shapes.cpp
triangulation.cpp
)
shapes.cpp
triangulation.cpp
)
......@@ -19,21 +19,17 @@ namespace fggl::math::phs3d {
void AABB::emtpy() {
min = {FLT_MAX, FLT_MAX, FLT_MAX};
max = { - FLT_MAX, - FLT_MAX, -FLT_MAX};
max = {-FLT_MAX, -FLT_MAX, -FLT_MAX};
}
void AABB::add(const math::vec3 &p) {
if ( p.x < min.x ) min.x = p.x;
if ( p.x > max.x ) max.x = p.x;
if ( p.y < min.y ) min.y = p.y;
if ( p.y > min.y ) max.y = p.y;
if ( p.z < min.z ) min.z = p.z;
if ( p.z > max.z ) max.z = p.z;
void AABB::add(const math::vec3 &point) {
min = minElm(min, point);
max = maxElm(max, point);
}
AABB AABB::fromPoints(const std::vector<math::vec3> &points) {
auto AABB::fromPoints(const std::vector<math::vec3> &points) -> AABB {
AABB box;
for (const auto& point : points) {
for (const auto &point : points) {
box.add(point);
}
return box;
......@@ -44,9 +40,9 @@ namespace fggl::math::phs3d {
min = max = m[3]; // should be the translation component of the matrix
// this feels like something that should be vectorizable...
for ( int i = 0; i < 3; i++) {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if ( m[i][j] > 0.0f ) {
if (m[i][j] > 0.0F) {
min[j] += m[i][j] * other.min[j];
max[j] += m[i][j] * other.max[j];
} else {
......@@ -57,7 +53,7 @@ namespace fggl::math::phs3d {
}
}
Plane Plane::fromPoints(const math::vec3 p1, const math::vec3 p2, const math::vec3 p3) {
auto Plane::fromPoints(const math::vec3 p1, const math::vec3 p2, const math::vec3 p3) -> Plane {
const auto e3 = p2 - p1;
const auto e1 = p3 - p2;
auto normal = glm::normalize(glm::cross(e3, e1));
......@@ -65,13 +61,13 @@ namespace fggl::math::phs3d {
return {normal, d};
}
static math::vec3 bestFitNormal(const std::vector<math::vec3>& points) {
static auto bestFitNormal(const std::vector<math::vec3> &points) -> math::vec3 {
assert(!points.empty());
math::vec3 result;
math::vec3 p = points.back();
for ( std::size_t i = 0; i < points.size(); ++i ){
for (std::size_t i = 0; i < points.size(); ++i) {
math::vec3 c = points[i];
result.x += (p.z + c.z) * (p.y - c.y);
......@@ -84,13 +80,13 @@ namespace fggl::math::phs3d {
return glm::normalize(result);
};
static float bestFitD(const std::vector<math::vec3>& points, glm::vec3 normal) {
static auto bestFitD(const std::vector<math::vec3> &points, glm::vec3 normal) -> float {
math::vec3 sum;
for (auto& point : points) {
for (const auto &point : points) {
sum += point;
}
sum *= 1.0F/points.size();
return glm::dot( sum, normal );
sum *= 1.0F / points.size();
return glm::dot(sum, normal);
}
const char X = 0;
......@@ -102,11 +98,11 @@ namespace fggl::math::phs3d {
const char b;
};
static bary_axis baryCalcAxis(const math::vec3& normal) {
if ( (fabs(normal.x) >= fabs(normal.y)) && (fabs(normal.x) >= fabs(normal.z))) {
static auto baryCalcAxis(const math::vec3 &normal) -> bary_axis {
if ((fabs(normal.x) >= fabs(normal.y)) && (fabs(normal.x) >= fabs(normal.z))) {
// discard x
return {Y, Z};
} else if ( fabs(normal.y) >= fabs(normal.z) ) {
} else if (fabs(normal.y) >= fabs(normal.z)) {
// discard y
return {Z, X};
} else {
......@@ -115,7 +111,7 @@ namespace fggl::math::phs3d {
}
}
bool Triangle::CartToBarycentric(const math::vec3& cart, Barycentric& outVal) {
auto Triangle::CartToBarycentric(const math::vec3 &cart, Barycentric &outVal) -> bool {
// everything is const because I'm paying the compiler is smarter than me...
const auto d1 = v[1] - v[0];
......@@ -137,20 +133,20 @@ namespace fggl::math::phs3d {
const float v3 = cart[ax.b] - v[0][ax.b];
const float v4 = cart[ax.b] - v[2][ax.b];
const float denom = v1*u2 - v2*u1;
if ( denom == 0.0f) {
const float denom = v1 * u2 - v2 * u1;
if (denom == 0.0F) {
return false;
}
// finally, we can work it out
const float oneOverDenom = 1.0f / denom;
outVal.b[0] = (v4*u2 - v2*u4) * oneOverDenom;
outVal.b[1] = (v1*u3 - v3*u1) * oneOverDenom;
outVal.b[2] = 1.0f - outVal.b[0] - outVal.b[1];
const float oneOverDenom = 1.0F / denom;
outVal.b[0] = (v4 * u2 - v2 * u4) * oneOverDenom;
outVal.b[1] = (v1 * u3 - v3 * u1) * oneOverDenom;
outVal.b[2] = 1.0F - outVal.b[0] - outVal.b[1];
return true;
}
bool Triangle::CartToBarycentric2(const math::vec3& cart, Barycentric& outVal) {
auto Triangle::CartToBarycentric2(const math::vec3 &cart, Barycentric &outVal) -> bool {
const auto e1 = v[2] - v[1];
const auto e2 = v[0] - v[2];
const auto e3 = v[1] - v[0];
......@@ -161,7 +157,7 @@ namespace fggl::math::phs3d {
const auto normal = glm::normalize(glm::cross(e1, e2));
const auto denom = glm::dot(glm::cross(e1, e2), normal);
assert( denom != 0.0f);
assert(denom != 0.0F);
outVal.b[0] = glm::dot(glm::cross(e1, d3), normal) / denom;
outVal.b[1] = glm::dot(glm::cross(e2, d1), normal) / denom;
......
......@@ -13,14 +13,13 @@
*/
#include "fggl/math/triangulation.hpp"
#include <iostream>
namespace fggl::math {
/**
* Fast Triangulation for convex polygons.
*/
void fan_triangulation(const PolygonVertex& polygon, data::Mesh2D &mesh) {
void fan_triangulation(const PolygonVertex &polygon, data::Mesh2D &mesh) {
assert(polygon.size() >= 3);
// add the first two points to the mesh
......@@ -29,7 +28,7 @@ namespace fggl::math {
// deal with the indices
const auto nTris = polygon.size() - 2;
for (auto i = 0u; i < nTris; i++) {
for (auto i = 0U; i < nTris; i++) {
mesh.add_index(firstIdx);
mesh.add_index(prevIdx);
......
target_sources(fggl
PRIVATE
null.cpp
)
\ No newline at end of file
null.cpp
)
\ No newline at end of file
......@@ -20,7 +20,7 @@
namespace fggl::phys {
bool NullPhysics::factory(modules::ModuleService serviceName, modules::Services &serviceManager) {
auto NullPhysics::factory(modules::ServiceName serviceName, modules::Services &serviceManager) -> bool {
if (serviceName == phys::PhysicsProvider::service) {
serviceManager.bind<phys::PhysicsProvider, NullPhysicsProvider>();
return true;
......
if ( CMAKE_SYSTEM_NAME MATCHES "Linux" )
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
add_subdirectory(linux)
else()
else ()
add_subdirectory(fallback)
endif()
\ No newline at end of file
endif ()
\ No newline at end of file
......@@ -16,22 +16,24 @@
// Created by webpigeon on 26/06/22.
//
#define FGGL_PLATFORM_PATHS fallback
#include <cstdlib>
#include "fggl/platform/fallback/paths.hpp"
namespace fggl::platform {
inline static std::filesystem::path get_user_path(const char* env, const char* fallback) {
const char* path = std::getenv(env);
inline static std::filesystem::path get_user_path(const char *env, const char *fallback) {
const char *path = std::getenv(env);
if (path != nullptr) {
return {path};
}
return std::filesystem::current_path() / fallback;
}
EnginePaths calc_engine_paths(const char* base) {
return EnginePaths {
EnginePaths calc_engine_paths(const char *base) {
return EnginePaths{
get_user_path(ENV_USER_CONFIG, DEFAULT_USER_CONFIG) / base,
get_user_path(ENV_USER_DATA, DEFAULT_USER_DATA) / base,
std::filesystem::temp_directory_path() / base
......@@ -40,13 +42,13 @@ namespace fggl::platform {
std::filesystem::path locate_data(const EnginePaths &paths, const std::filesystem::path &relPath) {
auto userPath = paths.userData / relPath;
if ( std::filesystem::exists(userPath) ) {
if (std::filesystem::exists(userPath)) {
return userPath;
}
// if debug mode, try CWD as well.
auto debugPath = std::filesystem::current_path() / "data" / relPath;
if ( std::filesystem::exists(debugPath) ) {
if (std::filesystem::exists(debugPath)) {
return debugPath;
}
......
#find_package(Fontconfig)
#target_link_libraries(fggl PRIVATE Fontconfig::Fontconfig)
target_sources( fggl
PRIVATE
# fonts.cpp
target_sources(fggl
PRIVATE
# fonts.cpp
paths.cpp
)
\ No newline at end of file
)
\ No newline at end of file
......@@ -21,14 +21,14 @@
namespace fggl::platform::Linux {
void get_font() {
/* FcConfig *config = FcInitLoadConfigAndFonts();
FcPattern* pat = FcPatternCreate();
FcObjectSet* os = FcObjectSetBuild(FC_FAMILY, FC_STYLE, FC_WEIGHT, FC_SLANT, FC_PIXEL_SIZE, FC_SIZE, nullptr);
FcFontSet* fs = FcFontList(config, pat, os);
/* FcConfig *config = FcInitLoadConfigAndFonts();
FcPattern* pat = FcPatternCreate();
FcObjectSet* os = FcObjectSetBuild(FC_FAMILY, FC_STYLE, FC_WEIGHT, FC_SLANT, FC_PIXEL_SIZE, FC_SIZE, nullptr);
FcFontSet* fs = FcFontList(config, pat, os);
if ( fs ) {
FcFontSetDestroy(fs);
}*/
if ( fs ) {
FcFontSetDestroy(fs);
}*/
}
} // namespace fggl::platform::linux
\ No newline at end of file
......@@ -18,54 +18,58 @@
#include <cstdlib>
#define FGGL_PLATFORM_PATHS linux
#include "fggl/platform/linux/paths.hpp"
#include "fggl/debug/logging.hpp"
namespace fggl::platform {
inline static std::filesystem::path get_user_path(const char* env, const char* fallback) {
const char* path = std::getenv(env);
if (path != nullptr) {
return {path};
namespace {
inline auto get_user_path(const char *env, const char *fallback) -> std::filesystem::path {
const char *path = std::getenv(env);
if (path != nullptr) {
return {path};
}
return {fallback};
}
return {fallback};
}
static std::vector<std::filesystem::path> get_path_list(const char* env, const char* folderName) {
const char* pathList = std::getenv(env);
std::vector<std::filesystem::path> paths;
if (pathList) {
std::string pathListStr(pathList);
std::string::size_type pos = 0;
while (pos < pathListStr.size()) {
std::string::size_type nextPos = pathListStr.find(':', pos);
if (nextPos == std::string::npos) {
nextPos = pathListStr.size();
auto get_path_list(const char *env, const char *folderName) -> std::vector<std::filesystem::path> {
const char *pathList = std::getenv(env);
std::vector<std::filesystem::path> paths;
if (pathList) {
std::string pathListStr(pathList);
std::string::size_type pos = 0;
while (pos < pathListStr.size()) {
std::string::size_type nextPos = pathListStr.find(':', pos);
if (nextPos == std::string::npos) {
nextPos = pathListStr.size();
}
std::string path = pathListStr.substr(pos, nextPos - pos);
paths.push_back(std::filesystem::path(path) / folderName);
pos = nextPos + 1;
}
std::string path = pathListStr.substr(pos, nextPos - pos);
paths.push_back(std::filesystem::path(path) / folderName);
pos = nextPos + 1;
}
return paths;
}
return paths;
}
EnginePaths calc_engine_paths(const char* base) {
auto calc_engine_paths(const char *base) -> EnginePaths {
auto dataDirs = get_path_list(ENV_DATA_DIRS, base);
if ( dataDirs.empty() ) {
for ( const auto& defaultDir : DEFAULT_DATA_DIRS ) {
dataDirs.push_back(std::filesystem::path(defaultDir) / base );
if (dataDirs.empty()) {
for (const auto &defaultDir : DEFAULT_DATA_DIRS) {
dataDirs.push_back(std::filesystem::path(defaultDir) / base);
}
}
auto configDirs = get_path_list(ENV_CONFIG_DIRS, base);
if ( configDirs.empty() ) {
for ( const auto& defaultDir : DEFAULT_CONFIG_DIRS ) {
configDirs.push_back(std::filesystem::path(defaultDir) / base );
if (configDirs.empty()) {
for (const auto &defaultDir : DEFAULT_CONFIG_DIRS) {
configDirs.push_back(std::filesystem::path(defaultDir) / base);
}
}
return EnginePaths {
return EnginePaths{
get_user_path(ENV_USER_CONFIG, DEFAULT_USER_CONFIG) / base,
get_user_path(ENV_USER_DATA, DEFAULT_USER_DATA) / base,
get_user_path(ENV_USER_CACHE, DEFAULT_USER_CACHE) / base,
......@@ -74,42 +78,42 @@ namespace fggl::platform {
};
}
std::filesystem::path locate_data(const EnginePaths &paths, const std::filesystem::path &relPath) {
auto locate_data(const EnginePaths &paths, const std::filesystem::path &relPath) -> std::filesystem::path {
auto userPath = paths.userData / relPath;
if ( std::filesystem::exists(userPath) ) {
if (std::filesystem::exists(userPath)) {
return userPath;
}
// check system paths
for ( const auto& path : paths.dataDirs ) {
for (const auto &path : paths.dataDirs) {
auto fullPath = path / relPath;
debug::trace("Checking data path: {}, exists: {}", fullPath.c_str(), std::filesystem::exists(fullPath));
if ( std::filesystem::exists(fullPath) ) {
if (std::filesystem::exists(fullPath)) {
return fullPath;
}
}
// if debug mode, try CWD as well.
auto debugPath = std::filesystem::current_path() / "data" / relPath;
if ( std::filesystem::exists(debugPath) ) {
if (std::filesystem::exists(debugPath)) {
debug::trace("Checking debug path: {}, exists: {}", debugPath.c_str(), std::filesystem::exists(debugPath));
return debugPath;
}
// if the file existed, it shoudl exist in the user space
// if the file existed, it should exist in the user space
return userPath;
}
std::filesystem::path locate_config(const EnginePaths &paths, const std::filesystem::path &relPath) {
auto locate_config(const EnginePaths &paths, const std::filesystem::path &relPath) -> std::filesystem::path {
auto userPath = paths.userConfig / relPath;
if ( std::filesystem::exists(userPath) ) {
if (std::filesystem::exists(userPath)) {
return userPath;
}
// check system paths
for ( const auto& path : paths.configDirs ) {
for (const auto &path : paths.configDirs) {
auto fullPath = path / relPath;
if ( std::filesystem::exists(fullPath) ) {
if (std::filesystem::exists(fullPath)) {
return fullPath;
}
}
......@@ -118,16 +122,16 @@ namespace fggl::platform {
return userPath;
}
std::filesystem::path locate_cache(const EnginePaths &paths, const std::filesystem::path &relPath) {
auto locate_cache(const EnginePaths &paths, const std::filesystem::path &relPath) -> std::filesystem::path {
auto userPath = paths.userCache / relPath;
if ( std::filesystem::exists(userPath) ) {
if (std::filesystem::exists(userPath)) {
return userPath;
}
// check system paths
for ( const auto& path : paths.configDirs ) {
for (const auto &path : paths.configDirs) {
auto fullPath = path / relPath;
if ( std::filesystem::exists(fullPath) ) {
if (std::filesystem::exists(fullPath)) {
return fullPath;
}
}
......
......@@ -24,6 +24,20 @@
namespace fggl::scenes {
GameBase::GameBase(fggl::App& app) : AppState(app) {
m_input = app.service<input::Input>();
}
void GameBase::update(float /*dt*/) {
// detect the user quitting
if (m_input != nullptr) {
bool escapePressed = m_input->keyboard.pressed(glfwGetKeyScancode(GLFW_KEY_ESCAPE));
if (escapePressed) {
m_owner.change_state(m_previous);
}
}
}
Game::Game(fggl::App &app) : AppState(app) {
m_input = app.service<input::Input>();
}
......@@ -35,8 +49,8 @@ namespace fggl::scenes {
m_world = std::make_unique<entity::EntityManager>();
#ifdef FGGL_MODULE_BULLET
// FIXME this ties bullet to the game state - which shouldn't be the case
m_phys = std::make_unique<fggl::phys::bullet::BulletPhysicsEngine>(m_world.get());
// FIXME this ties bullet to the game state - which shouldn't be the case
m_phys = std::make_unique<fggl::phys::bullet::BulletPhysicsEngine>(m_world.get());
#endif
}
......@@ -45,26 +59,31 @@ namespace fggl::scenes {
m_world.reset();
}
void Game::update() {
assert( m_world && "called game update, but there was no world - was activate called?" );
void Game::update(float /*dt*/) {
assert(m_world && "called game update, but there was no world - was activate called?");
if ( m_input != nullptr ) {
if (m_input != nullptr) {
bool escapePressed = m_input->keyboard.pressed(glfwGetKeyScancode(GLFW_KEY_ESCAPE));
if (escapePressed) {
m_owner.change_state(m_previous);
}
if ( m_input->keyboard.pressed(glfwGetKeyScancode(GLFW_KEY_F2)) ) {
m_debug = !m_debug;
}
}
if ( m_phys != nullptr ) {
if (m_phys != nullptr) {
m_phys->step();
}
// debug render toggle
//m_world->reapEntities();
}
void Game::render(fggl::gfx::Graphics &gfx) {
if ( m_world != nullptr ) {
gfx.drawScene( *m_world );
if (m_world != nullptr) {
gfx.drawScene(*m_world, m_debug);
}
}
......
......@@ -19,84 +19,68 @@
namespace fggl::scenes {
using fggl::input::MouseButton;
using fggl::input::MouseAxis;
using fggl::input::MouseButton;
using fggl::input::MouseAxis;
constexpr float SCREEN_WIDTH = 1920.0F;
constexpr float SCREEN_HEIGHT = 1080.0F;
BasicMenu::BasicMenu(fggl::App &app) : AppState(app), m_inputs(app.service<input::Input>()), m_active(), m_cursorPos(), m_hover(nullptr) {
}
BasicMenu::BasicMenu(fggl::App& app) : AppState(app), m_inputs(nullptr), m_active(), m_hover(nullptr) {
m_inputs = app.service<input::Input>();
}
void BasicMenu::update() {
if ( m_inputs != nullptr ) {
m_cursorPos.x = m_inputs->mouse.axis( MouseAxis::X );
m_cursorPos.y = m_inputs->mouse.axis( MouseAxis::Y );
void BasicMenu::update(float /*dt*/) {
if (m_inputs != nullptr) {
m_cursorPos.x = m_inputs->mouse.axis(MouseAxis::X);
m_cursorPos.y = m_inputs->mouse.axis(MouseAxis::Y);
// in canvas space
math::vec2 projected;
projected.x = math::rescale_ndc(m_cursorPos.x, 0, 1920.f);
//projected.y = math::rescale_ndc(m_cursorPos.y, 1080.0f, 0);
projected.y = math::rescale_ndc(m_cursorPos.y, 0, 1080.0f);
auto* hoverWidget = m_canvas.getChildAt(projected);
if ( hoverWidget != m_hover ){
if ( m_hover != nullptr ) {
m_hover->onExit();
}
m_hover = hoverWidget;
if ( m_hover != nullptr ){
m_hover->onEnter();
}
}
projected.x = math::rescale_ndc(m_cursorPos.x, 0, SCREEN_WIDTH);
projected.y = math::rescale_ndc(m_cursorPos.y, 0, SCREEN_HEIGHT);
m_canvas.onMouseOver(projected);
if ( m_inputs->mouse.pressed( MouseButton::LEFT ) ) {
spdlog::info("clicky clicky: ({}, {})", projected.x, projected.y);
auto widget = m_canvas.getChildAt(projected);
if (m_inputs->mouse.pressed(MouseButton::LEFT)) {
auto* widget = m_canvas.getChildAt(projected);
if (widget != nullptr) {
widget->activate();
spdlog::info("ooo! there is a thing there!");
}
}
}
}
}
}
}
void BasicMenu::render(gfx::Graphics& gfx) {
void BasicMenu::render(gfx::Graphics &gfx) {
// render the 2D scene (we don't have a 3D scene to worry about in menus)
gfx::Paint paint;
m_canvas.render( paint );
m_canvas.render(paint);
gfx.draw2D(paint);
}
}
void BasicMenu::activate() {
void BasicMenu::activate() {
}
}
void BasicMenu::deactivate() {
void BasicMenu::deactivate() {
}
}
void BasicMenu::add(const std::string& name, callback cb) {
m_items[name] = cb;
void BasicMenu::add(const std::string &name, const Callback& callback) {
m_items[name] = callback;
const math::vec2 btnSize{ 150.0f, 30.0f };
const math::vec2 btnSize{150.0F, 30.0F};
const float spacing = 5;
const float padX = 50.0f;
const float padY = 50.0f;
const float padX = 50.0F;
const float padY = 50.0F;
// figure out the position based off the old logic
// FIXME should be the container's job
math::vec2 pos { 1920.0f - ( padX + btnSize.x ), padY };
auto btnIdx = m_items.size() - 1;
pos.y += (btnIdx * (btnSize.y + spacing));
math::vec2 pos{SCREEN_WIDTH - (padX + btnSize.x), padY};
pos.y += ( (m_items.size()-1.0F) * (btnSize.y + spacing));
// build the button
auto btn = std::make_unique<gui::Button>(pos, btnSize);
btn->label(name);
btn->addCallback(cb);
btn->addCallback(callback);
m_canvas.add(std::move(btn));
}
}
};
......@@ -146,7 +146,7 @@ extern unsigned int stbcc_get_unique_id(stbcc_grid *g, int x, int y);
#include <string.h> // memset
#if !defined(STBCC_GRID_COUNT_X_LOG2) || !defined(STBCC_GRID_COUNT_Y_LOG2)
#error "You must define STBCC_GRID_COUNT_X_LOG2 and STBCC_GRID_COUNT_Y_LOG2 to define the max grid supported."
#error "You must define STBCC_GRID_COUNT_X_LOG2 and STBCC_GRID_COUNT_Y_LOG2 to define the max grid supported."
#endif
#define STBCC__GRID_COUNT_X (1 << STBCC_GRID_COUNT_X_LOG2)
......@@ -155,19 +155,19 @@ extern unsigned int stbcc_get_unique_id(stbcc_grid *g, int x, int y);
#define STBCC__MAP_STRIDE (1 << (STBCC_GRID_COUNT_X_LOG2-3))
#ifndef STBCC_CLUSTER_SIZE_X_LOG2
#define STBCC_CLUSTER_SIZE_X_LOG2 (STBCC_GRID_COUNT_X_LOG2/2) // log2(sqrt(2^N)) = 1/2 * log2(2^N)) = 1/2 * N
#if STBCC_CLUSTER_SIZE_X_LOG2 > 6
#undef STBCC_CLUSTER_SIZE_X_LOG2
#define STBCC_CLUSTER_SIZE_X_LOG2 6
#endif
#define STBCC_CLUSTER_SIZE_X_LOG2 (STBCC_GRID_COUNT_X_LOG2/2) // log2(sqrt(2^N)) = 1/2 * log2(2^N)) = 1/2 * N
#if STBCC_CLUSTER_SIZE_X_LOG2 > 6
#undef STBCC_CLUSTER_SIZE_X_LOG2
#define STBCC_CLUSTER_SIZE_X_LOG2 6
#endif
#endif
#ifndef STBCC_CLUSTER_SIZE_Y_LOG2
#define STBCC_CLUSTER_SIZE_Y_LOG2 (STBCC_GRID_COUNT_Y_LOG2/2)
#if STBCC_CLUSTER_SIZE_Y_LOG2 > 6
#undef STBCC_CLUSTER_SIZE_Y_LOG2
#define STBCC_CLUSTER_SIZE_Y_LOG2 6
#endif
#define STBCC_CLUSTER_SIZE_Y_LOG2 (STBCC_GRID_COUNT_Y_LOG2/2)
#if STBCC_CLUSTER_SIZE_Y_LOG2 > 6
#undef STBCC_CLUSTER_SIZE_Y_LOG2
#define STBCC_CLUSTER_SIZE_Y_LOG2 6
#endif
#endif
#define STBCC__CLUSTER_SIZE_X (1 << STBCC_CLUSTER_SIZE_X_LOG2)
......@@ -180,7 +180,7 @@ extern unsigned int stbcc_get_unique_id(stbcc_grid *g, int x, int y);
#define STBCC__CLUSTER_COUNT_Y (1 << STBCC__CLUSTER_COUNT_Y_LOG2)
#if STBCC__CLUSTER_SIZE_X >= STBCC__GRID_COUNT_X || STBCC__CLUSTER_SIZE_Y >= STBCC__GRID_COUNT_Y
#error "STBCC_CLUSTER_SIZE_X/Y_LOG2 must be smaller than STBCC_GRID_COUNT_X/Y_LOG2"
#error "STBCC_CLUSTER_SIZE_X/Y_LOG2 must be smaller than STBCC_GRID_COUNT_X/Y_LOG2"
#endif
// worst case # of clumps per cluster
......@@ -222,16 +222,16 @@ typedef unsigned char stbcc__verify_max_exits[STBCC__MAX_EXITS_PER_CLUMP <= 256]
typedef struct
{
unsigned short clump_index:12;
signed short cluster_dx:2;
signed short cluster_dy:2;
signed short cluster_dx:2;
signed short cluster_dy:2;
} stbcc__relative_clumpid;
typedef union
{
struct {
unsigned int clump_index:12;
unsigned int cluster_x:10;
unsigned int cluster_y:10;
unsigned int clump_index:12;
unsigned int cluster_x:10;
unsigned int cluster_y:10;
} f;
unsigned int c;
} stbcc__global_clumpid;
......@@ -280,11 +280,11 @@ int stbcc_query_grid_node_connection(stbcc_grid *g, int x1, int y1, int x2, int
int cy2 = STBCC__CLUSTER_Y_FOR_COORD_Y(y2);
assert(!g->in_batched_update);
if (c1 == STBCC__NULL_CLUMPID || c2 == STBCC__NULL_CLUMPID)
return 0;
return 0;
label1 = g->cluster[cy1][cx1].clump[c1].global_label;
label2 = g->cluster[cy2][cx2].clump[c2].global_label;
if (label1.c == label2.c)
return 1;
return 1;
return 0;
}
......@@ -324,7 +324,7 @@ static stbcc__global_clumpid stbcc__clump_find(stbcc_grid *g, stbcc__global_clum
stbcc__clump *c = &g->cluster[n.f.cluster_y][n.f.cluster_x].clump[n.f.clump_index];
if (c->global_label.c == n.c)
return n;
return n;
q = stbcc__clump_find(g, c->global_label);
c->global_label = q;
......@@ -346,7 +346,7 @@ static void stbcc__clump_union(stbcc_grid *g, stbcc__unpacked_clumpid m, int x,
stbcc__global_clumpid np = stbcc__clump_find(g, nc->global_label);
if (mp.c == np.c)
return;
return;
g->cluster[mp.f.cluster_y][mp.f.cluster_x].clump[mp.f.clump_index].global_label = np;
}
......@@ -356,51 +356,51 @@ static void stbcc__build_connected_components_for_clumps(stbcc_grid *g)
int i,j,k,h;
for (j=0; j < STBCC__CLUSTER_COUNT_Y; ++j) {
for (i=0; i < STBCC__CLUSTER_COUNT_X; ++i) {
stbcc__cluster *cluster = &g->cluster[j][i];
for (k=0; k < (int) cluster->num_edge_clumps; ++k) {
stbcc__global_clumpid m;
m.f.clump_index = k;
m.f.cluster_x = i;
m.f.cluster_y = j;
assert((int) m.f.clump_index == k && (int) m.f.cluster_x == i && (int) m.f.cluster_y == j);
cluster->clump[k].global_label = m;
}
}
for (i=0; i < STBCC__CLUSTER_COUNT_X; ++i) {
stbcc__cluster *cluster = &g->cluster[j][i];
for (k=0; k < (int) cluster->num_edge_clumps; ++k) {
stbcc__global_clumpid m;
m.f.clump_index = k;
m.f.cluster_x = i;
m.f.cluster_y = j;
assert((int) m.f.clump_index == k && (int) m.f.cluster_x == i && (int) m.f.cluster_y == j);
cluster->clump[k].global_label = m;
}
}
}
for (j=0; j < STBCC__CLUSTER_COUNT_Y; ++j) {
for (i=0; i < STBCC__CLUSTER_COUNT_X; ++i) {
stbcc__cluster *cluster = &g->cluster[j][i];
for (k=0; k < (int) cluster->num_edge_clumps; ++k) {
stbcc__clump *clump = &cluster->clump[k];
stbcc__unpacked_clumpid m;
stbcc__relative_clumpid *adj;
m.clump_index = k;
m.cluster_x = i;
m.cluster_y = j;
adj = &cluster->adjacency_storage[clump->adjacent_clump_list_index];
for (h=0; h < clump->num_adjacent; ++h) {
unsigned int clump_index = adj[h].clump_index;
unsigned int x = adj[h].cluster_dx + i;
unsigned int y = adj[h].cluster_dy + j;
stbcc__clump_union(g, m, x, y, clump_index);
}
}
}
for (i=0; i < STBCC__CLUSTER_COUNT_X; ++i) {
stbcc__cluster *cluster = &g->cluster[j][i];
for (k=0; k < (int) cluster->num_edge_clumps; ++k) {
stbcc__clump *clump = &cluster->clump[k];
stbcc__unpacked_clumpid m;
stbcc__relative_clumpid *adj;
m.clump_index = k;
m.cluster_x = i;
m.cluster_y = j;
adj = &cluster->adjacency_storage[clump->adjacent_clump_list_index];
for (h=0; h < clump->num_adjacent; ++h) {
unsigned int clump_index = adj[h].clump_index;
unsigned int x = adj[h].cluster_dx + i;
unsigned int y = adj[h].cluster_dy + j;
stbcc__clump_union(g, m, x, y, clump_index);
}
}
}
}
for (j=0; j < STBCC__CLUSTER_COUNT_Y; ++j) {
for (i=0; i < STBCC__CLUSTER_COUNT_X; ++i) {
stbcc__cluster *cluster = &g->cluster[j][i];
for (k=0; k < (int) cluster->num_edge_clumps; ++k) {
stbcc__global_clumpid m;
m.f.clump_index = k;
m.f.cluster_x = i;
m.f.cluster_y = j;
stbcc__clump_find(g, m);
}
}
for (i=0; i < STBCC__CLUSTER_COUNT_X; ++i) {
stbcc__cluster *cluster = &g->cluster[j][i];
for (k=0; k < (int) cluster->num_edge_clumps; ++k) {
stbcc__global_clumpid m;
m.f.clump_index = k;
m.f.cluster_x = i;
m.f.cluster_y = j;
stbcc__clump_find(g, m);
}
}
}
}
......@@ -422,57 +422,57 @@ static void stbcc__build_all_connections_for_cluster(stbcc_grid *g, int cx, int
total = 0;
for (m=0; m < 4; ++m) {
switch (m) {
case 0:
dx = 1, dy = 0;
step_x = 0, step_y = 1;
i = STBCC__CLUSTER_SIZE_X-1;
j = 0;
n = STBCC__CLUSTER_SIZE_Y;
break;
case 1:
dx = -1, dy = 0;
i = 0;
j = 0;
step_x = 0;
step_y = 1;
n = STBCC__CLUSTER_SIZE_Y;
break;
case 2:
dy = -1, dx = 0;
i = 0;
j = 0;
step_x = 1;
step_y = 0;
n = STBCC__CLUSTER_SIZE_X;
break;
case 3:
dy = 1, dx = 0;
i = 0;
j = STBCC__CLUSTER_SIZE_Y-1;
step_x = 1;
step_y = 0;
n = STBCC__CLUSTER_SIZE_X;
break;
}
if (cx+dx < 0 || cx+dx >= g->cw || cy+dy < 0 || cy+dy >= g->ch)
continue;
memset(connected, 0, sizeof(connected));
for (k=0; k < n; ++k) {
if (STBCC__MAP_OPEN(g, x+i, y+j) && STBCC__MAP_OPEN(g, x+i+dx, y+j+dy)) {
stbcc__clumpid src = g->clump_for_node[y+j][x+i];
stbcc__clumpid dest = g->clump_for_node[y+j+dy][x+i+dx];
if (0 == (connected[src][dest>>3] & (1 << (dest & 7)))) {
connected[src][dest>>3] |= 1 << (dest & 7);
++num_adj[src];
++total;
}
}
i += step_x;
j += step_y;
}
switch (m) {
case 0:
dx = 1, dy = 0;
step_x = 0, step_y = 1;
i = STBCC__CLUSTER_SIZE_X-1;
j = 0;
n = STBCC__CLUSTER_SIZE_Y;
break;
case 1:
dx = -1, dy = 0;
i = 0;
j = 0;
step_x = 0;
step_y = 1;
n = STBCC__CLUSTER_SIZE_Y;
break;
case 2:
dy = -1, dx = 0;
i = 0;
j = 0;
step_x = 1;
step_y = 0;
n = STBCC__CLUSTER_SIZE_X;
break;
case 3:
dy = 1, dx = 0;
i = 0;
j = STBCC__CLUSTER_SIZE_Y-1;
step_x = 1;
step_y = 0;
n = STBCC__CLUSTER_SIZE_X;
break;
}
if (cx+dx < 0 || cx+dx >= g->cw || cy+dy < 0 || cy+dy >= g->ch)
continue;
memset(connected, 0, sizeof(connected));
for (k=0; k < n; ++k) {
if (STBCC__MAP_OPEN(g, x+i, y+j) && STBCC__MAP_OPEN(g, x+i+dx, y+j+dy)) {
stbcc__clumpid src = g->clump_for_node[y+j][x+i];
stbcc__clumpid dest = g->clump_for_node[y+j+dy][x+i+dx];
if (0 == (connected[src][dest>>3] & (1 << (dest & 7)))) {
connected[src][dest>>3] |= 1 << (dest & 7);
++num_adj[src];
++total;
}
}
i += step_x;
j += step_y;
}
}
assert(total <= STBCC__CLUSTER_ADJACENCY_COUNT);
......@@ -487,24 +487,24 @@ static void stbcc__build_all_connections_for_cluster(stbcc_grid *g, int cx, int
// ignoring edge-vs-non-edge with 'num_adj[i]*2' was faster than
// 'num_adj[i]+extra' with the divide
if (total + (cluster->num_edge_clumps<<2) <= STBCC__CLUSTER_ADJACENCY_COUNT)
extra = 4;
extra = 4;
else if (total + (cluster->num_edge_clumps<<1) <= STBCC__CLUSTER_ADJACENCY_COUNT)
extra = 2;
extra = 2;
else if (total + (cluster->num_edge_clumps<<0) <= STBCC__CLUSTER_ADJACENCY_COUNT)
extra = 1;
extra = 1;
else
extra = 0;
extra = 0;
total = 0;
for (i=0; i < (int) cluster->num_edge_clumps; ++i) {
int alloc = num_adj[i]+extra;
if (alloc > STBCC__MAX_EXITS_PER_CLUSTER)
alloc = STBCC__MAX_EXITS_PER_CLUSTER;
assert(total < 256); // must fit in byte
cluster->clump[i].adjacent_clump_list_index = (unsigned char) total;
cluster->clump[i].max_adjacent = alloc;
cluster->clump[i].num_adjacent = 0;
total += alloc;
int alloc = num_adj[i]+extra;
if (alloc > STBCC__MAX_EXITS_PER_CLUSTER)
alloc = STBCC__MAX_EXITS_PER_CLUSTER;
assert(total < 256); // must fit in byte
cluster->clump[i].adjacent_clump_list_index = (unsigned char) total;
cluster->clump[i].max_adjacent = alloc;
cluster->clump[i].num_adjacent = 0;
total += alloc;
}
assert(total <= STBCC__CLUSTER_ADJACENCY_COUNT);
......@@ -519,9 +519,9 @@ static void stbcc__build_all_connections_for_cluster(stbcc_grid *g, int cx, int
static void stbcc__add_connections_to_adjacent_cluster_with_rebuild(stbcc_grid *g, int cx, int cy, int dx, int dy)
{
if (cx >= 0 && cx < g->cw && cy >= 0 && cy < g->ch) {
stbcc__add_connections_to_adjacent_cluster(g, cx, cy, dx, dy);
if (g->cluster[cy][cx].rebuild_adjacency)
stbcc__build_all_connections_for_cluster(g, cx, cy);
stbcc__add_connections_to_adjacent_cluster(g, cx, cy, dx, dy);
if (g->cluster[cy][cx].rebuild_adjacency)
stbcc__build_all_connections_for_cluster(g, cx, cy);
}
}
......@@ -530,11 +530,11 @@ void stbcc_update_grid(stbcc_grid *g, int x, int y, int solid)
int cx,cy;
if (!solid) {
if (STBCC__MAP_OPEN(g,x,y))
return;
if (STBCC__MAP_OPEN(g,x,y))
return;
} else {
if (!STBCC__MAP_OPEN(g,x,y))
return;
if (!STBCC__MAP_OPEN(g,x,y))
return;
}
cx = STBCC__CLUSTER_X_FOR_COORD_X(x);
......@@ -546,9 +546,9 @@ void stbcc_update_grid(stbcc_grid *g, int x, int y, int solid)
stbcc__remove_connections_to_adjacent_cluster(g, cx, cy+1, 0,-1);
if (!solid)
STBCC__MAP_BYTE(g,x,y) |= STBCC__MAP_BYTE_MASK(x,y);
STBCC__MAP_BYTE(g,x,y) |= STBCC__MAP_BYTE_MASK(x,y);
else
STBCC__MAP_BYTE(g,x,y) &= ~STBCC__MAP_BYTE_MASK(x,y);
STBCC__MAP_BYTE(g,x,y) &= ~STBCC__MAP_BYTE_MASK(x,y);
stbcc__build_clumps_for_cluster(g, cx, cy);
stbcc__build_all_connections_for_cluster(g, cx, cy);
......@@ -559,11 +559,11 @@ void stbcc_update_grid(stbcc_grid *g, int x, int y, int solid)
stbcc__add_connections_to_adjacent_cluster_with_rebuild(g, cx, cy+1, 0,-1);
if (!g->in_batched_update)
stbcc__build_connected_components_for_clumps(g);
#if 0
stbcc__build_connected_components_for_clumps(g);
#if 0
else
g->cluster_dirty[cy][cx] = 1;
#endif
g->cluster_dirty[cy][cx] = 1;
#endif
}
void stbcc_update_batch_begin(stbcc_grid *g)
......@@ -597,35 +597,35 @@ void stbcc_init_grid(stbcc_grid *g, unsigned char *map, int w, int h)
g->ch = h >> STBCC_CLUSTER_SIZE_Y_LOG2;
g->in_batched_update = 0;
#if 0
#if 0
for (j=0; j < STBCC__CLUSTER_COUNT_Y; ++j)
for (i=0; i < STBCC__CLUSTER_COUNT_X; ++i)
g->cluster_dirty[j][i] = 0;
#endif
for (i=0; i < STBCC__CLUSTER_COUNT_X; ++i)
g->cluster_dirty[j][i] = 0;
#endif
for (j=0; j < h; ++j) {
for (i=0; i < w; i += 8) {
unsigned char c = 0;
for (k=0; k < 8; ++k)
if (map[j*w + (i+k)] == 0)
c |= (1 << k);
g->map[j][i>>3] = c;
}
for (i=0; i < w; i += 8) {
unsigned char c = 0;
for (k=0; k < 8; ++k)
if (map[j*w + (i+k)] == 0)
c |= (1 << k);
g->map[j][i>>3] = c;
}
}
for (j=0; j < g->ch; ++j)
for (i=0; i < g->cw; ++i)
stbcc__build_clumps_for_cluster(g, i, j);
for (i=0; i < g->cw; ++i)
stbcc__build_clumps_for_cluster(g, i, j);
for (j=0; j < g->ch; ++j)
for (i=0; i < g->cw; ++i)
stbcc__build_all_connections_for_cluster(g, i, j);
for (i=0; i < g->cw; ++i)
stbcc__build_all_connections_for_cluster(g, i, j);
stbcc__build_connected_components_for_clumps(g);
for (j=0; j < g->h; ++j)
for (i=0; i < g->w; ++i)
assert(g->clump_for_node[j][i] <= STBCC__NULL_CLUMPID);
for (i=0; i < g->w; ++i)
assert(g->clump_for_node[j][i] <= STBCC__NULL_CLUMPID);
}
......@@ -657,12 +657,12 @@ static void stbcc__add_clump_connection(stbcc_grid *g, int x1, int y1, int x2, i
clump = &cluster->clump[c1];
assert(clump->num_adjacent <= clump->max_adjacent);
if (clump->num_adjacent == clump->max_adjacent)
g->cluster[cy1][cx1].rebuild_adjacency = 1;
g->cluster[cy1][cx1].rebuild_adjacency = 1;
else {
stbcc__relative_clumpid *adj = &cluster->adjacency_storage[clump->adjacent_clump_list_index];
assert(clump->num_adjacent < STBCC__MAX_EXITS_PER_CLUMP);
assert(clump->adjacent_clump_list_index + clump->num_adjacent <= STBCC__CLUSTER_ADJACENCY_COUNT);
adj[clump->num_adjacent++] = rc;
stbcc__relative_clumpid *adj = &cluster->adjacency_storage[clump->adjacent_clump_list_index];
assert(clump->num_adjacent < STBCC__MAX_EXITS_PER_CLUMP);
assert(clump->adjacent_clump_list_index + clump->num_adjacent <= STBCC__CLUSTER_ADJACENCY_COUNT);
adj[clump->num_adjacent++] = rc;
}
}
......@@ -697,15 +697,15 @@ static void stbcc__remove_clump_connection(stbcc_grid *g, int x1, int y1, int x2
adj = &cluster->adjacency_storage[clump->adjacent_clump_list_index];
for (i=0; i < clump->num_adjacent; ++i)
if (rc.clump_index == adj[i].clump_index &&
rc.cluster_dx == adj[i].cluster_dx &&
rc.cluster_dy == adj[i].cluster_dy)
break;
if (rc.clump_index == adj[i].clump_index &&
rc.cluster_dx == adj[i].cluster_dx &&
rc.cluster_dy == adj[i].cluster_dy)
break;
if (i < clump->num_adjacent)
adj[i] = adj[--clump->num_adjacent];
adj[i] = adj[--clump->num_adjacent];
else
assert(0);
assert(0);
}
static void stbcc__add_connections_to_adjacent_cluster(stbcc_grid *g, int cx, int cy, int dx, int dy)
......@@ -716,59 +716,59 @@ static void stbcc__add_connections_to_adjacent_cluster(stbcc_grid *g, int cx, in
int step_x, step_y=0, i, j, k, n;
if (cx < 0 || cx >= g->cw || cy < 0 || cy >= g->ch)
return;
return;
if (cx+dx < 0 || cx+dx >= g->cw || cy+dy < 0 || cy+dy >= g->ch)
return;
return;
if (g->cluster[cy][cx].rebuild_adjacency)
return;
return;
assert(abs(dx) + abs(dy) == 1);
if (dx == 1) {
i = STBCC__CLUSTER_SIZE_X-1;
j = 0;
step_x = 0;
step_y = 1;
n = STBCC__CLUSTER_SIZE_Y;
i = STBCC__CLUSTER_SIZE_X-1;
j = 0;
step_x = 0;
step_y = 1;
n = STBCC__CLUSTER_SIZE_Y;
} else if (dx == -1) {
i = 0;
j = 0;
step_x = 0;
step_y = 1;
n = STBCC__CLUSTER_SIZE_Y;
i = 0;
j = 0;
step_x = 0;
step_y = 1;
n = STBCC__CLUSTER_SIZE_Y;
} else if (dy == -1) {
i = 0;
j = 0;
step_x = 1;
step_y = 0;
n = STBCC__CLUSTER_SIZE_X;
i = 0;
j = 0;
step_x = 1;
step_y = 0;
n = STBCC__CLUSTER_SIZE_X;
} else if (dy == 1) {
i = 0;
j = STBCC__CLUSTER_SIZE_Y-1;
step_x = 1;
step_y = 0;
n = STBCC__CLUSTER_SIZE_X;
i = 0;
j = STBCC__CLUSTER_SIZE_Y-1;
step_x = 1;
step_y = 0;
n = STBCC__CLUSTER_SIZE_X;
} else {
assert(0);
return;
assert(0);
return;
}
for (k=0; k < n; ++k) {
if (STBCC__MAP_OPEN(g, x+i, y+j) && STBCC__MAP_OPEN(g, x+i+dx, y+j+dy)) {
stbcc__clumpid src = g->clump_for_node[y+j][x+i];
stbcc__clumpid dest = g->clump_for_node[y+j+dy][x+i+dx];
if (0 == (connected[src][dest>>3] & (1 << (dest & 7)))) {
assert((dest>>3) < sizeof(connected));
connected[src][dest>>3] |= 1 << (dest & 7);
stbcc__add_clump_connection(g, x+i, y+j, x+i+dx, y+j+dy);
if (g->cluster[cy][cx].rebuild_adjacency)
break;
}
}
i += step_x;
j += step_y;
if (STBCC__MAP_OPEN(g, x+i, y+j) && STBCC__MAP_OPEN(g, x+i+dx, y+j+dy)) {
stbcc__clumpid src = g->clump_for_node[y+j][x+i];
stbcc__clumpid dest = g->clump_for_node[y+j+dy][x+i+dx];
if (0 == (connected[src][dest>>3] & (1 << (dest & 7)))) {
assert((dest>>3) < sizeof(connected));
connected[src][dest>>3] |= 1 << (dest & 7);
stbcc__add_clump_connection(g, x+i, y+j, x+i+dx, y+j+dy);
if (g->cluster[cy][cx].rebuild_adjacency)
break;
}
}
i += step_x;
j += step_y;
}
}
......@@ -780,53 +780,53 @@ static void stbcc__remove_connections_to_adjacent_cluster(stbcc_grid *g, int cx,
int step_x, step_y=0, i, j, k, n;
if (cx < 0 || cx >= g->cw || cy < 0 || cy >= g->ch)
return;
return;
if (cx+dx < 0 || cx+dx >= g->cw || cy+dy < 0 || cy+dy >= g->ch)
return;
return;
assert(abs(dx) + abs(dy) == 1);
if (dx == 1) {
i = STBCC__CLUSTER_SIZE_X-1;
j = 0;
step_x = 0;
step_y = 1;
n = STBCC__CLUSTER_SIZE_Y;
i = STBCC__CLUSTER_SIZE_X-1;
j = 0;
step_x = 0;
step_y = 1;
n = STBCC__CLUSTER_SIZE_Y;
} else if (dx == -1) {
i = 0;
j = 0;
step_x = 0;
step_y = 1;
n = STBCC__CLUSTER_SIZE_Y;
i = 0;
j = 0;
step_x = 0;
step_y = 1;
n = STBCC__CLUSTER_SIZE_Y;
} else if (dy == -1) {
i = 0;
j = 0;
step_x = 1;
step_y = 0;
n = STBCC__CLUSTER_SIZE_X;
i = 0;
j = 0;
step_x = 1;
step_y = 0;
n = STBCC__CLUSTER_SIZE_X;
} else if (dy == 1) {
i = 0;
j = STBCC__CLUSTER_SIZE_Y-1;
step_x = 1;
step_y = 0;
n = STBCC__CLUSTER_SIZE_X;
i = 0;
j = STBCC__CLUSTER_SIZE_Y-1;
step_x = 1;
step_y = 0;
n = STBCC__CLUSTER_SIZE_X;
} else {
assert(0);
return;
assert(0);
return;
}
for (k=0; k < n; ++k) {
if (STBCC__MAP_OPEN(g, x+i, y+j) && STBCC__MAP_OPEN(g, x+i+dx, y+j+dy)) {
stbcc__clumpid src = g->clump_for_node[y+j][x+i];
stbcc__clumpid dest = g->clump_for_node[y+j+dy][x+i+dx];
if (0 == (disconnected[src][dest>>3] & (1 << (dest & 7)))) {
disconnected[src][dest>>3] |= 1 << (dest & 7);
stbcc__remove_clump_connection(g, x+i, y+j, x+i+dx, y+j+dy);
}
}
i += step_x;
j += step_y;
if (STBCC__MAP_OPEN(g, x+i, y+j) && STBCC__MAP_OPEN(g, x+i+dx, y+j+dy)) {
stbcc__clumpid src = g->clump_for_node[y+j][x+i];
stbcc__clumpid dest = g->clump_for_node[y+j+dy][x+i+dx];
if (0 == (disconnected[src][dest>>3] & (1 << (dest & 7)))) {
disconnected[src][dest>>3] |= 1 << (dest & 7);
stbcc__remove_clump_connection(g, x+i, y+j, x+i+dx, y+j+dy);
}
}
i += step_x;
j += step_y;
}
}
......@@ -835,7 +835,7 @@ static stbcc__tinypoint stbcc__incluster_find(stbcc__cluster_build_info *cbi, in
stbcc__tinypoint p,q;
p = cbi->parent[y][x];
if (p.x == x && p.y == y)
return p;
return p;
q = stbcc__incluster_find(cbi, p.x, p.y);
cbi->parent[y][x] = q;
return q;
......@@ -847,7 +847,7 @@ static void stbcc__incluster_union(stbcc__cluster_build_info *cbi, int x1, int y
stbcc__tinypoint q = stbcc__incluster_find(cbi, x2,y2);
if (p.x == q.x && p.y == q.y)
return;
return;
cbi->parent[p.y][p.x] = q;
}
......@@ -871,23 +871,23 @@ static void stbcc__build_clumps_for_cluster(stbcc_grid *g, int cx, int cy)
// set initial disjoint set forest state
for (j=0; j < STBCC__CLUSTER_SIZE_Y; ++j) {
for (i=0; i < STBCC__CLUSTER_SIZE_X; ++i) {
cbi.parent[j][i].x = i;
cbi.parent[j][i].y = j;
}
for (i=0; i < STBCC__CLUSTER_SIZE_X; ++i) {
cbi.parent[j][i].x = i;
cbi.parent[j][i].y = j;
}
}
// join all sets that are connected
for (j=0; j < STBCC__CLUSTER_SIZE_Y; ++j) {
// check down only if not on bottom row
if (j < STBCC__CLUSTER_SIZE_Y-1)
for (i=0; i < STBCC__CLUSTER_SIZE_X; ++i)
if (STBCC__MAP_OPEN(g,x+i,y+j) && STBCC__MAP_OPEN(g,x+i ,y+j+1))
stbcc__incluster_union(&cbi, i,j, i,j+1);
// check right for everything but rightmost column
for (i=0; i < STBCC__CLUSTER_SIZE_X-1; ++i)
if (STBCC__MAP_OPEN(g,x+i,y+j) && STBCC__MAP_OPEN(g,x+i+1,y+j ))
stbcc__incluster_union(&cbi, i,j, i+1,j);
// check down only if not on bottom row
if (j < STBCC__CLUSTER_SIZE_Y-1)
for (i=0; i < STBCC__CLUSTER_SIZE_X; ++i)
if (STBCC__MAP_OPEN(g,x+i,y+j) && STBCC__MAP_OPEN(g,x+i ,y+j+1))
stbcc__incluster_union(&cbi, i,j, i,j+1);
// check right for everything but rightmost column
for (i=0; i < STBCC__CLUSTER_SIZE_X-1; ++i)
if (STBCC__MAP_OPEN(g,x+i,y+j) && STBCC__MAP_OPEN(g,x+i+1,y+j ))
stbcc__incluster_union(&cbi, i,j, i+1,j);
}
// label all non-empty clumps along edges so that all edge clumps are first
......@@ -897,57 +897,57 @@ static void stbcc__build_clumps_for_cluster(stbcc_grid *g, int cx, int cy)
// first put solid labels on all the edges; these will get overwritten if they're open
for (j=0; j < STBCC__CLUSTER_SIZE_Y; ++j)
cbi.label[j][0] = cbi.label[j][STBCC__CLUSTER_SIZE_X-1] = STBCC__NULL_CLUMPID;
cbi.label[j][0] = cbi.label[j][STBCC__CLUSTER_SIZE_X-1] = STBCC__NULL_CLUMPID;
for (i=0; i < STBCC__CLUSTER_SIZE_X; ++i)
cbi.label[0][i] = cbi.label[STBCC__CLUSTER_SIZE_Y-1][i] = STBCC__NULL_CLUMPID;
cbi.label[0][i] = cbi.label[STBCC__CLUSTER_SIZE_Y-1][i] = STBCC__NULL_CLUMPID;
for (j=0; j < STBCC__CLUSTER_SIZE_Y; ++j) {
i = 0;
if (STBCC__MAP_OPEN(g, x+i, y+j)) {
stbcc__tinypoint p = stbcc__incluster_find(&cbi, i,j);
if (p.x == i && p.y == j)
// if this is the leader, give it a label
cbi.label[j][i] = label++;
else if (!(p.x == 0 || p.x == STBCC__CLUSTER_SIZE_X-1 || p.y == 0 || p.y == STBCC__CLUSTER_SIZE_Y-1)) {
// if leader is in interior, promote this edge node to leader and label
stbcc__switch_root(&cbi, i, j, p);
cbi.label[j][i] = label++;
}
// else if leader is on edge, do nothing (it'll get labelled when we reach it)
}
i = STBCC__CLUSTER_SIZE_X-1;
if (STBCC__MAP_OPEN(g, x+i, y+j)) {
stbcc__tinypoint p = stbcc__incluster_find(&cbi, i,j);
if (p.x == i && p.y == j)
cbi.label[j][i] = label++;
else if (!(p.x == 0 || p.x == STBCC__CLUSTER_SIZE_X-1 || p.y == 0 || p.y == STBCC__CLUSTER_SIZE_Y-1)) {
stbcc__switch_root(&cbi, i, j, p);
cbi.label[j][i] = label++;
}
}
i = 0;
if (STBCC__MAP_OPEN(g, x+i, y+j)) {
stbcc__tinypoint p = stbcc__incluster_find(&cbi, i,j);
if (p.x == i && p.y == j)
// if this is the leader, give it a label
cbi.label[j][i] = label++;
else if (!(p.x == 0 || p.x == STBCC__CLUSTER_SIZE_X-1 || p.y == 0 || p.y == STBCC__CLUSTER_SIZE_Y-1)) {
// if leader is in interior, promote this edge node to leader and label
stbcc__switch_root(&cbi, i, j, p);
cbi.label[j][i] = label++;
}
// else if leader is on edge, do nothing (it'll get labelled when we reach it)
}
i = STBCC__CLUSTER_SIZE_X-1;
if (STBCC__MAP_OPEN(g, x+i, y+j)) {
stbcc__tinypoint p = stbcc__incluster_find(&cbi, i,j);
if (p.x == i && p.y == j)
cbi.label[j][i] = label++;
else if (!(p.x == 0 || p.x == STBCC__CLUSTER_SIZE_X-1 || p.y == 0 || p.y == STBCC__CLUSTER_SIZE_Y-1)) {
stbcc__switch_root(&cbi, i, j, p);
cbi.label[j][i] = label++;
}
}
}
for (i=1; i < STBCC__CLUSTER_SIZE_Y-1; ++i) {
j = 0;
if (STBCC__MAP_OPEN(g, x+i, y+j)) {
stbcc__tinypoint p = stbcc__incluster_find(&cbi, i,j);
if (p.x == i && p.y == j)
cbi.label[j][i] = label++;
else if (!(p.x == 0 || p.x == STBCC__CLUSTER_SIZE_X-1 || p.y == 0 || p.y == STBCC__CLUSTER_SIZE_Y-1)) {
stbcc__switch_root(&cbi, i, j, p);
cbi.label[j][i] = label++;
}
}
j = STBCC__CLUSTER_SIZE_Y-1;
if (STBCC__MAP_OPEN(g, x+i, y+j)) {
stbcc__tinypoint p = stbcc__incluster_find(&cbi, i,j);
if (p.x == i && p.y == j)
cbi.label[j][i] = label++;
else if (!(p.x == 0 || p.x == STBCC__CLUSTER_SIZE_X-1 || p.y == 0 || p.y == STBCC__CLUSTER_SIZE_Y-1)) {
stbcc__switch_root(&cbi, i, j, p);
cbi.label[j][i] = label++;
}
}
j = 0;
if (STBCC__MAP_OPEN(g, x+i, y+j)) {
stbcc__tinypoint p = stbcc__incluster_find(&cbi, i,j);
if (p.x == i && p.y == j)
cbi.label[j][i] = label++;
else if (!(p.x == 0 || p.x == STBCC__CLUSTER_SIZE_X-1 || p.y == 0 || p.y == STBCC__CLUSTER_SIZE_Y-1)) {
stbcc__switch_root(&cbi, i, j, p);
cbi.label[j][i] = label++;
}
}
j = STBCC__CLUSTER_SIZE_Y-1;
if (STBCC__MAP_OPEN(g, x+i, y+j)) {
stbcc__tinypoint p = stbcc__incluster_find(&cbi, i,j);
if (p.x == i && p.y == j)
cbi.label[j][i] = label++;
else if (!(p.x == 0 || p.x == STBCC__CLUSTER_SIZE_X-1 || p.y == 0 || p.y == STBCC__CLUSTER_SIZE_Y-1)) {
stbcc__switch_root(&cbi, i, j, p);
cbi.label[j][i] = label++;
}
}
}
c = &g->cluster[cy][cx];
......@@ -955,51 +955,51 @@ static void stbcc__build_clumps_for_cluster(stbcc_grid *g, int cx, int cy)
// label any internal clusters
for (j=1; j < STBCC__CLUSTER_SIZE_Y-1; ++j) {
for (i=1; i < STBCC__CLUSTER_SIZE_X-1; ++i) {
stbcc__tinypoint p = cbi.parent[j][i];
if (p.x == i && p.y == j) {
if (STBCC__MAP_OPEN(g,x+i,y+j))
cbi.label[j][i] = label++;
else
cbi.label[j][i] = STBCC__NULL_CLUMPID;
}
}
for (i=1; i < STBCC__CLUSTER_SIZE_X-1; ++i) {
stbcc__tinypoint p = cbi.parent[j][i];
if (p.x == i && p.y == j) {
if (STBCC__MAP_OPEN(g,x+i,y+j))
cbi.label[j][i] = label++;
else
cbi.label[j][i] = STBCC__NULL_CLUMPID;
}
}
}
// label all other nodes
for (j=0; j < STBCC__CLUSTER_SIZE_Y; ++j) {
for (i=0; i < STBCC__CLUSTER_SIZE_X; ++i) {
stbcc__tinypoint p = stbcc__incluster_find(&cbi, i,j);
if (p.x != i || p.y != j) {
if (STBCC__MAP_OPEN(g,x+i,y+j))
cbi.label[j][i] = cbi.label[p.y][p.x];
}
if (STBCC__MAP_OPEN(g,x+i,y+j))
assert(cbi.label[j][i] != STBCC__NULL_CLUMPID);
}
for (i=0; i < STBCC__CLUSTER_SIZE_X; ++i) {
stbcc__tinypoint p = stbcc__incluster_find(&cbi, i,j);
if (p.x != i || p.y != j) {
if (STBCC__MAP_OPEN(g,x+i,y+j))
cbi.label[j][i] = cbi.label[p.y][p.x];
}
if (STBCC__MAP_OPEN(g,x+i,y+j))
assert(cbi.label[j][i] != STBCC__NULL_CLUMPID);
}
}
c->num_clumps = label;
for (i=0; i < label; ++i) {
c->clump[i].num_adjacent = 0;
c->clump[i].max_adjacent = 0;
c->clump[i].num_adjacent = 0;
c->clump[i].max_adjacent = 0;
}
for (j=0; j < STBCC__CLUSTER_SIZE_Y; ++j)
for (i=0; i < STBCC__CLUSTER_SIZE_X; ++i) {
g->clump_for_node[y+j][x+i] = cbi.label[j][i]; // @OPTIMIZE: remove cbi.label entirely
assert(g->clump_for_node[y+j][x+i] <= STBCC__NULL_CLUMPID);
}
for (i=0; i < STBCC__CLUSTER_SIZE_X; ++i) {
g->clump_for_node[y+j][x+i] = cbi.label[j][i]; // @OPTIMIZE: remove cbi.label entirely
assert(g->clump_for_node[y+j][x+i] <= STBCC__NULL_CLUMPID);
}
// set the global label for all interior clumps since they can't have connections,
// so we don't have to do this on the global pass (brings from O(N) to O(N^0.75))
for (i=(int) c->num_edge_clumps; i < (int) c->num_clumps; ++i) {
stbcc__global_clumpid gc;
gc.f.cluster_x = cx;
gc.f.cluster_y = cy;
gc.f.clump_index = i;
c->clump[i].global_label = gc;
stbcc__global_clumpid gc;
gc.f.cluster_x = cx;
gc.f.cluster_y = cy;
gc.f.clump_index = i;
c->clump[i].global_label = gc;
}
c->rebuild_adjacency = 1; // flag that it has no valid adjacency data
......
......@@ -93,10 +93,10 @@ extern "C" {
extern int stb_div_trunc(int value_to_be_divided, int value_to_divide_by);
extern int stb_div_floor(int value_to_be_divided, int value_to_divide_by);
extern int stb_div_eucl (int value_to_be_divided, int value_to_divide_by);
extern int stb_div_eucl(int value_to_be_divided, int value_to_divide_by);
extern int stb_mod_trunc(int value_to_be_divided, int value_to_divide_by);
extern int stb_mod_floor(int value_to_be_divided, int value_to_divide_by);
extern int stb_mod_eucl (int value_to_be_divided, int value_to_divide_by);
extern int stb_mod_eucl(int value_to_be_divided, int value_to_divide_by);
#ifdef __cplusplus
}
......@@ -105,9 +105,9 @@ extern int stb_mod_eucl (int value_to_be_divided, int value_to_divide_by);
#ifdef STB_DIVIDE_IMPLEMENTATION
#if defined(__STDC_VERSION) && __STDC_VERSION__ >= 19901
#ifndef C_INTEGER_DIVISION_TRUNCATES
#define C_INTEGER_DIVISION_TRUNCATES
#endif
#ifndef C_INTEGER_DIVISION_TRUNCATES
#define C_INTEGER_DIVISION_TRUNCATES
#endif
#endif
#ifndef INT_MIN
......@@ -117,150 +117,150 @@ extern int stb_mod_eucl (int value_to_be_divided, int value_to_divide_by);
// the following macros are designed to allow testing
// other platforms by simulating them
#ifndef STB_DIVIDE_TEST_FLOOR
#define stb__div(a,b) ((a)/(b))
#define stb__mod(a,b) ((a)%(b))
#define stb__div(a,b) ((a)/(b))
#define stb__mod(a,b) ((a)%(b))
#else
// implement floor-style divide on trunc platform
#ifndef C_INTEGER_DIVISION_TRUNCATES
#error "floor test requires truncating division"
#endif
#undef C_INTEGER_DIVISION_TRUNCATES
#ifndef C_INTEGER_DIVISION_TRUNCATES
#error "floor test requires truncating division"
#endif
#undef C_INTEGER_DIVISION_TRUNCATES
int stb__div(int v1, int v2)
{
int q = v1/v2, r = v1%v2;
if ((r > 0 && v2 < 0) || (r < 0 && v2 > 0))
return q-1;
else
return q;
int q = v1/v2, r = v1%v2;
if ((r > 0 && v2 < 0) || (r < 0 && v2 > 0))
return q-1;
else
return q;
}
int stb__mod(int v1, int v2)
{
int r = v1%v2;
if ((r > 0 && v2 < 0) || (r < 0 && v2 > 0))
return r+v2;
else
return r;
int r = v1%v2;
if ((r > 0 && v2 < 0) || (r < 0 && v2 > 0))
return r+v2;
else
return r;
}
#endif
int stb_div_trunc(int v1, int v2)
{
#ifdef C_INTEGER_DIVISION_TRUNCATES
#ifdef C_INTEGER_DIVISION_TRUNCATES
return v1/v2;
#else
#else
if (v1 >= 0 && v2 <= 0)
return -stb__div(-v1,v2); // both negative to avoid overflow
return -stb__div(-v1,v2); // both negative to avoid overflow
if (v1 <= 0 && v2 >= 0)
if (v1 != INT_MIN)
return -stb__div(v1,-v2); // both negative to avoid overflow
else
return -stb__div(v1+v2,-v2)-1; // push v1 away from wrap point
if (v1 != INT_MIN)
return -stb__div(v1,-v2); // both negative to avoid overflow
else
return -stb__div(v1+v2,-v2)-1; // push v1 away from wrap point
else
return v1/v2; // same sign, so expect truncation
#endif
return v1/v2; // same sign, so expect truncation
#endif
}
int stb_div_floor(int v1, int v2)
{
#ifdef C_INTEGER_DIVISION_FLOORS
#ifdef C_INTEGER_DIVISION_FLOORS
return v1/v2;
#else
#else
if (v1 >= 0 && v2 < 0) {
if (v2 + 1 >= INT_MIN + v1) // check if increasing v1's magnitude overflows
return -stb__div((v2+1)-v1,v2); // nope, so just compute it
else
return -stb__div(-v1,v2) + ((-v1)%v2 ? -1 : 0);
if (v2 + 1 >= INT_MIN + v1) // check if increasing v1's magnitude overflows
return -stb__div((v2+1)-v1,v2); // nope, so just compute it
else
return -stb__div(-v1,v2) + ((-v1)%v2 ? -1 : 0);
}
if (v1 < 0 && v2 >= 0) {
if (v1 != INT_MIN) {
if (v1 + 1 >= INT_MIN + v2) // check if increasing v1's magnitude overflows
return -stb__div((v1+1)-v2,-v2); // nope, so just compute it
else
return -stb__div(-v1,v2) + (stb__mod(v1,-v2) ? -1 : 0);
} else // it must be possible to compute -(v1+v2) without overflowing
return -stb__div(-(v1+v2),v2) + (stb__mod(-(v1+v2),v2) ? -2 : -1);
if (v1 != INT_MIN) {
if (v1 + 1 >= INT_MIN + v2) // check if increasing v1's magnitude overflows
return -stb__div((v1+1)-v2,-v2); // nope, so just compute it
else
return -stb__div(-v1,v2) + (stb__mod(v1,-v2) ? -1 : 0);
} else // it must be possible to compute -(v1+v2) without overflowing
return -stb__div(-(v1+v2),v2) + (stb__mod(-(v1+v2),v2) ? -2 : -1);
} else
return v1/v2; // same sign, so expect truncation
#endif
return v1/v2; // same sign, so expect truncation
#endif
}
int stb_div_eucl(int v1, int v2)
{
int q,r;
#ifdef C_INTEGER_DIVISION_TRUNCATES
#ifdef C_INTEGER_DIVISION_TRUNCATES
q = v1/v2;
r = v1%v2;
#else
#else
// handle every quadrant separately, since we can't rely on q and r flor
if (v1 >= 0)
if (v2 >= 0)
return stb__div(v1,v2);
else if (v2 != INT_MIN)
q = -stb__div(v1,-v2), r = stb__mod(v1,-v2);
else
q = 0, r = v1;
if (v2 >= 0)
return stb__div(v1,v2);
else if (v2 != INT_MIN)
q = -stb__div(v1,-v2), r = stb__mod(v1,-v2);
else
q = 0, r = v1;
else if (v1 != INT_MIN)
if (v2 >= 0)
q = -stb__div(-v1,v2), r = -stb__mod(-v1,v2);
else if (v2 != INT_MIN)
q = stb__div(-v1,-v2), r = -stb__mod(-v1,-v2);
else // if v2 is INT_MIN, then we can't use -v2, but we can't divide by v2
q = 1, r = v1-q*v2;
if (v2 >= 0)
q = -stb__div(-v1,v2), r = -stb__mod(-v1,v2);
else if (v2 != INT_MIN)
q = stb__div(-v1,-v2), r = -stb__mod(-v1,-v2);
else // if v2 is INT_MIN, then we can't use -v2, but we can't divide by v2
q = 1, r = v1-q*v2;
else // if v1 is INT_MIN, we have to move away from overflow place
if (v2 >= 0)
q = -stb__div(-(v1+v2),v2)-1, r = -stb__mod(-(v1+v2),v2);
else if (v2 != INT_MIN)
q = stb__div(-(v1-v2),-v2)+1, r = -stb__mod(-(v1-v2),-v2);
else // for INT_MIN / INT_MIN, we need to be extra-careful to avoid overflow
q = 1, r = 0;
#endif
if (v2 >= 0)
q = -stb__div(-(v1+v2),v2)-1, r = -stb__mod(-(v1+v2),v2);
else if (v2 != INT_MIN)
q = stb__div(-(v1-v2),-v2)+1, r = -stb__mod(-(v1-v2),-v2);
else // for INT_MIN / INT_MIN, we need to be extra-careful to avoid overflow
q = 1, r = 0;
#endif
if (r >= 0)
return q;
return q;
else
return q + (v2 > 0 ? -1 : 1);
return q + (v2 > 0 ? -1 : 1);
}
int stb_mod_trunc(int v1, int v2)
{
#ifdef C_INTEGER_DIVISION_TRUNCATES
#ifdef C_INTEGER_DIVISION_TRUNCATES
return v1%v2;
#else
#else
if (v1 >= 0) { // modulus result should always be positive
int r = stb__mod(v1,v2);
if (r >= 0)
return r;
else
return r - (v2 < 0 ? v2 : -v2);
int r = stb__mod(v1,v2);
if (r >= 0)
return r;
else
return r - (v2 < 0 ? v2 : -v2);
} else { // modulus result should always be negative
int r = stb__mod(v1,v2);
if (r <= 0)
return r;
else
return r + (v2 < 0 ? v2 : -v2);
int r = stb__mod(v1,v2);
if (r <= 0)
return r;
else
return r + (v2 < 0 ? v2 : -v2);
}
#endif
#endif
}
int stb_mod_floor(int v1, int v2)
{
#ifdef C_INTEGER_DIVISION_FLOORS
#ifdef C_INTEGER_DIVISION_FLOORS
return v1%v2;
#else
#else
if (v2 >= 0) { // result should always be positive
int r = stb__mod(v1,v2);
if (r >= 0)
return r;
else
return r + v2;
int r = stb__mod(v1,v2);
if (r >= 0)
return r;
else
return r + v2;
} else { // result should always be negative
int r = stb__mod(v1,v2);
if (r <= 0)
return r;
else
return r + v2;
int r = stb__mod(v1,v2);
if (r <= 0)
return r;
else
return r + v2;
}
#endif
#endif
}
int stb_mod_eucl(int v1, int v2)
......@@ -268,9 +268,9 @@ int stb_mod_eucl(int v1, int v2)
int r = stb__mod(v1,v2);
if (r >= 0)
return r;
return r;
else
return r - (v2 < 0 ? v2 : -v2); // negative abs() [to avoid overflow]
return r - (v2 < 0 ? v2 : -v2); // negative abs() [to avoid overflow]
}
#ifdef STB_DIVIDE_TEST
......@@ -284,28 +284,28 @@ int err=0;
void stbdiv_check(int q, int r, int a, int b, char *type, int dir)
{
if ((dir > 0 && r < 0) || (dir < 0 && r > 0)) {
fprintf(stderr, "FAILED: %s(%d,%d) remainder %d in wrong direction\n", type,a,b,r);
err++;
fprintf(stderr, "FAILED: %s(%d,%d) remainder %d in wrong direction\n", type,a,b,r);
err++;
} else
if (b != INT_MIN) // can't compute abs(), but if b==INT_MIN all remainders are valid
if (r <= -abs(b) || r >= abs(b)) {
fprintf(stderr, "FAILED: %s(%d,%d) remainder %d out of range\n", type,a,b,r);
err++;
}
#ifdef STB_DIVIDE_TEST_64
if (b != INT_MIN) // can't compute abs(), but if b==INT_MIN all remainders are valid
if (r <= -abs(b) || r >= abs(b)) {
fprintf(stderr, "FAILED: %s(%d,%d) remainder %d out of range\n", type,a,b,r);
err++;
}
#ifdef STB_DIVIDE_TEST_64
{
STB_DIVIDE_TEST_64 q64 = q, r64=r, a64=a, b64=b;
if (q64*b64+r64 != a64) {
fprintf(stderr, "FAILED: %s(%d,%d) remainder %d doesn't match quotient %d\n", type,a,b,r,q);
err++;
}
STB_DIVIDE_TEST_64 q64 = q, r64=r, a64=a, b64=b;
if (q64*b64+r64 != a64) {
fprintf(stderr, "FAILED: %s(%d,%d) remainder %d doesn't match quotient %d\n", type,a,b,r,q);
err++;
}
}
#else
#else
if (q*b+r != a) {
fprintf(stderr, "FAILED: %s(%d,%d) remainder %d doesn't match quotient %d\n", type,a,b,r,q);
err++;
fprintf(stderr, "FAILED: %s(%d,%d) remainder %d doesn't match quotient %d\n", type,a,b,r,q);
err++;
}
#endif
#endif
}
void test(int a, int b)
......
......@@ -256,21 +256,19 @@ STB_HEXWAVE_DEF void hexwave_generate_samples(float *output, int num_samples, He
// oscillator_freq: frequency of the oscillator divided by the sample rate
// private:
typedef struct
{
int reflect;
float peak_time;
float zero_wait;
float half_height;
typedef struct {
int reflect;
float peak_time;
float zero_wait;
float half_height;
} HexWaveParameters;
struct HexWave
{
float t, prev_dt;
HexWaveParameters current, pending;
int have_pending;
float buffer[STB_HEXWAVE_MAX_BLEP_LENGTH];
};
struct HexWave {
float t, prev_dt;
HexWaveParameters current, pending;
int have_pending;
float buffer[STB_HEXWAVE_MAX_BLEP_LENGTH];
};
#endif
#ifdef STB_HEXWAVE_IMPLEMENTATION
......@@ -320,14 +318,14 @@ static void hex_add_oversampled_bleplike(float *output, float time_since_transit
int slot = (int) (time_since_transition * hexblep.oversample);
if (slot >= hexblep.oversample)
slot = hexblep.oversample-1; // clamp in case the floats overshoot
slot = hexblep.oversample-1; // clamp in case the floats overshoot
d1 = &data[ slot *bw];
d2 = &data[(slot+1)*bw];
lerpweight = time_since_transition * hexblep.oversample - slot;
for (i=0; i < bw; ++i)
output[i] += scale * (d1[i] + (d2[i]-d1[i])*lerpweight);
output[i] += scale * (d1[i] + (d2[i]-d1[i])*lerpweight);
}
static void hex_blep (float *output, float time_since_transition, float scale)
......@@ -362,52 +360,52 @@ static void hexwave_generate_linesegs(hexvert vert[9], HexWave *hex, float dt)
vert[3].v = hex->current.half_height;
if (hex->current.reflect) {
for (j=4; j <= 7; ++j) {
vert[j].t = 1 - vert[7-j].t;
vert[j].v = - vert[7-j].v;
}
for (j=4; j <= 7; ++j) {
vert[j].t = 1 - vert[7-j].t;
vert[j].v = - vert[7-j].v;
}
} else {
for (j=4; j <= 7; ++j) {
vert[j].t = 0.5f + vert[j-4].t;
vert[j].v = - vert[j-4].v;
}
for (j=4; j <= 7; ++j) {
vert[j].t = 0.5f + vert[j-4].t;
vert[j].v = - vert[j-4].v;
}
}
vert[8].t = 1;
vert[8].v = 0;
for (j=0; j < 8; ++j) {
if (vert[j+1].t <= vert[j].t + min_len) {
// if change takes place over less than a fraction of a sample treat as discontinuity
//
// otherwise the slope computation can blow up to arbitrarily large and we
// try to generate a huge BLAMP and the result is wrong.
//
// why does this happen if the math is right? i believe if done perfectly,
// the two BLAMPs on either side of the slope would cancel out, but our
// BLAMPs have only limited sub-sample precision and limited integration
// accuracy. or maybe it's just the math blowing up w/ floating point precision
// limits as we try to make x * (1/x) cancel out
//
// min_len verified artifact-free even near nyquist with only oversample=4
vert[j+1].t = vert[j].t;
}
if (vert[j+1].t <= vert[j].t + min_len) {
// if change takes place over less than a fraction of a sample treat as discontinuity
//
// otherwise the slope computation can blow up to arbitrarily large and we
// try to generate a huge BLAMP and the result is wrong.
//
// why does this happen if the math is right? i believe if done perfectly,
// the two BLAMPs on either side of the slope would cancel out, but our
// BLAMPs have only limited sub-sample precision and limited integration
// accuracy. or maybe it's just the math blowing up w/ floating point precision
// limits as we try to make x * (1/x) cancel out
//
// min_len verified artifact-free even near nyquist with only oversample=4
vert[j+1].t = vert[j].t;
}
}
if (vert[8].t != 1.0f) {
// if the above fixup moved the endpoint away from 1.0, move it back,
// along with any other vertices that got moved to the same time
float t = vert[8].t;
for (j=5; j <= 8; ++j)
if (vert[j].t == t)
vert[j].t = 1.0f;
// if the above fixup moved the endpoint away from 1.0, move it back,
// along with any other vertices that got moved to the same time
float t = vert[8].t;
for (j=5; j <= 8; ++j)
if (vert[j].t == t)
vert[j].t = 1.0f;
}
// compute the exact slopes from the final fixed-up positions
for (j=0; j < 8; ++j)
if (vert[j+1].t == vert[j].t)
vert[j].s = 0;
else
vert[j].s = (vert[j+1].v - vert[j].v) / (vert[j+1].t - vert[j].t);
if (vert[j+1].t == vert[j].t)
vert[j].s = 0;
else
vert[j].s = (vert[j+1].v - vert[j].v) / (vert[j+1].t - vert[j].t);
// wraparound at end
vert[8].t = 1;
......@@ -429,21 +427,21 @@ STB_HEXWAVE_DEF void hexwave_generate_samples(float *output, int num_samples, He
// all sample times are biased by halfw to leave room for BLEP/BLAMP to go back in time
if (num_samples <= 0)
return;
return;
// convert parameters to times and slopes
hexwave_generate_linesegs(vert, hex, dt);
if (hex->prev_dt != dt) {
// if frequency changes, add a fixup at the derivative discontinuity starting at now
float slope;
for (j=1; j < 6; ++j)
if (t < vert[j].t)
break;
slope = vert[j].s;
if (slope != 0)
hex_blamp(output, 0, (dt - hex->prev_dt)*slope);
hex->prev_dt = dt;
// if frequency changes, add a fixup at the derivative discontinuity starting at now
float slope;
for (j=1; j < 6; ++j)
if (t < vert[j].t)
break;
slope = vert[j].s;
if (slope != 0)
hex_blamp(output, 0, (dt - hex->prev_dt)*slope);
hex->prev_dt = dt;
}
// copy the buffered data from last call and clear the rest of the output array
......@@ -451,93 +449,93 @@ STB_HEXWAVE_DEF void hexwave_generate_samples(float *output, int num_samples, He
memset(temp_output, 0, 2*hexblep.width*sizeof(float));
if (num_samples >= hexblep.width) {
memcpy(output, hex->buffer, buffered_length);
memcpy(output, hex->buffer, buffered_length);
} else {
// if the output is shorter than hexblep.width, we do all synthesis to temp_output
memcpy(temp_output, hex->buffer, buffered_length);
// if the output is shorter than hexblep.width, we do all synthesis to temp_output
memcpy(temp_output, hex->buffer, buffered_length);
}
for (pass=0; pass < 2; ++pass) {
int i0,i1;
float *out;
// we want to simulate having one buffer that is num_output + hexblep.width
// samples long, without putting that requirement on the user, and without
// allocating a temp buffer that's as long as the whole thing. so we use two
// overlapping buffers, one the user's buffer and one a fixed-length temp
// buffer.
if (pass == 0) {
if (num_samples < hexblep.width)
continue;
// run as far as we can without overwriting the end of the user's buffer
out = output;
i0 = 0;
i1 = num_samples - hexblep.width;
} else {
// generate the rest into a temp buffer
out = temp_output;
i0 = 0;
if (num_samples >= hexblep.width)
i1 = hexblep.width;
else
i1 = num_samples;
}
// determine current segment
for (j=0; j < 8; ++j)
if (t < vert[j+1].t)
break;
i = i0;
for(;;) {
while (t < vert[j+1].t) {
if (i == i1)
goto done;
out[i+halfw] += vert[j].v + vert[j].s*(t - vert[j].t);
t += dt;
++i;
}
// transition from lineseg starting at j to lineseg starting at j+1
if (vert[j].t == vert[j+1].t)
hex_blep(out+i, recip_dt*(t-vert[j+1].t), (vert[j+1].v - vert[j].v));
hex_blamp(out+i, recip_dt*(t-vert[j+1].t), dt*(vert[j+1].s - vert[j].s));
++j;
if (j == 8) {
// change to different waveform if there's a change pending
j = 0;
t -= 1.0; // t was >= 1.f if j==8
if (hex->have_pending) {
float prev_s0 = vert[j].s;
float prev_v0 = vert[j].v;
hex->current = hex->pending;
hex->have_pending = 0;
hexwave_generate_linesegs(vert, hex, dt);
// the following never occurs with this oscillator, but it makes
// the code work in more general cases
if (vert[j].v != prev_v0)
hex_blep (out+i, recip_dt*t, (vert[j].v - prev_v0));
if (vert[j].s != prev_s0)
hex_blamp(out+i, recip_dt*t, dt*(vert[j].s - prev_s0));
}
}
}
done:
;
int i0,i1;
float *out;
// we want to simulate having one buffer that is num_output + hexblep.width
// samples long, without putting that requirement on the user, and without
// allocating a temp buffer that's as long as the whole thing. so we use two
// overlapping buffers, one the user's buffer and one a fixed-length temp
// buffer.
if (pass == 0) {
if (num_samples < hexblep.width)
continue;
// run as far as we can without overwriting the end of the user's buffer
out = output;
i0 = 0;
i1 = num_samples - hexblep.width;
} else {
// generate the rest into a temp buffer
out = temp_output;
i0 = 0;
if (num_samples >= hexblep.width)
i1 = hexblep.width;
else
i1 = num_samples;
}
// determine current segment
for (j=0; j < 8; ++j)
if (t < vert[j+1].t)
break;
i = i0;
for(;;) {
while (t < vert[j+1].t) {
if (i == i1)
goto done;
out[i+halfw] += vert[j].v + vert[j].s*(t - vert[j].t);
t += dt;
++i;
}
// transition from lineseg starting at j to lineseg starting at j+1
if (vert[j].t == vert[j+1].t)
hex_blep(out+i, recip_dt*(t-vert[j+1].t), (vert[j+1].v - vert[j].v));
hex_blamp(out+i, recip_dt*(t-vert[j+1].t), dt*(vert[j+1].s - vert[j].s));
++j;
if (j == 8) {
// change to different waveform if there's a change pending
j = 0;
t -= 1.0; // t was >= 1.f if j==8
if (hex->have_pending) {
float prev_s0 = vert[j].s;
float prev_v0 = vert[j].v;
hex->current = hex->pending;
hex->have_pending = 0;
hexwave_generate_linesegs(vert, hex, dt);
// the following never occurs with this oscillator, but it makes
// the code work in more general cases
if (vert[j].v != prev_v0)
hex_blep (out+i, recip_dt*t, (vert[j].v - prev_v0));
if (vert[j].s != prev_s0)
hex_blamp(out+i, recip_dt*t, dt*(vert[j].s - prev_s0));
}
}
}
done:
;
}
// at this point, we've written output[] and temp_output[]
if (num_samples >= hexblep.width) {
// the first half of temp[] overlaps the end of output, the second half will be the new start overlap
for (i=0; i < hexblep.width; ++i)
output[num_samples-hexblep.width + i] += temp_output[i];
memcpy(hex->buffer, temp_output+hexblep.width, buffered_length);
// the first half of temp[] overlaps the end of output, the second half will be the new start overlap
for (i=0; i < hexblep.width; ++i)
output[num_samples-hexblep.width + i] += temp_output[i];
memcpy(hex->buffer, temp_output+hexblep.width, buffered_length);
} else {
for (i=0; i < num_samples; ++i)
output[i] = temp_output[i];
memcpy(hex->buffer, temp_output+num_samples, buffered_length);
for (i=0; i < num_samples; ++i)
output[i] = temp_output[i];
memcpy(hex->buffer, temp_output+num_samples, buffered_length);
}
hex->t = t;
......@@ -545,15 +543,15 @@ STB_HEXWAVE_DEF void hexwave_generate_samples(float *output, int num_samples, He
STB_HEXWAVE_DEF void hexwave_shutdown(float *user_buffer)
{
#ifndef STB_HEXWAVE_NO_ALLOCATION
#ifndef STB_HEXWAVE_NO_ALLOCATION
if (user_buffer != 0) {
free(hexblep.blep);
free(hexblep.blamp);
free(hexblep.blep);
free(hexblep.blamp);
}
#endif
#endif
}
// buffer should be NULL or must be 4*(width*(oversample+1)*2 +
// buffer should be NULL or must be 4*(width*(oversample+1)*2 +
STB_HEXWAVE_DEF void hexwave_init(int width, int oversample, float *user_buffer)
{
int halfwidth = width/2;
......@@ -572,57 +570,57 @@ STB_HEXWAVE_DEF void hexwave_init(int width, int oversample, float *user_buffer)
int i,j;
if (width > STB_HEXWAVE_MAX_BLEP_LENGTH)
width = STB_HEXWAVE_MAX_BLEP_LENGTH;
width = STB_HEXWAVE_MAX_BLEP_LENGTH;
if (user_buffer == 0) {
#ifndef STB_HEXWAVE_NO_ALLOCATION
blep_buffer = (float *) malloc(sizeof(float)*blep_buffer_count);
blamp_buffer = (float *) malloc(sizeof(float)*blep_buffer_count);
#endif
#ifndef STB_HEXWAVE_NO_ALLOCATION
blep_buffer = (float *) malloc(sizeof(float)*blep_buffer_count);
blamp_buffer = (float *) malloc(sizeof(float)*blep_buffer_count);
#endif
} else {
blep_buffer = ramp+n;
blamp_buffer = blep_buffer + blep_buffer_count;
blep_buffer = ramp+n;
blamp_buffer = blep_buffer + blep_buffer_count;
}
// compute BLEP and BLAMP by integerating windowed sinc
for (i=0; i < n; ++i) {
for (j=0; j < 16; ++j) {
float sinc_t = 3.141592f* (i-half) / oversample;
float sinc = (i==half) ? 1.0f : (float) sin(sinc_t) / (sinc_t);
float wt = 2.0f*3.1415926f * i / (n-1);
float window = (float) (0.355768 - 0.487396*cos(wt) + 0.144232*cos(2*wt) - 0.012604*cos(3*wt)); // Nuttall
double value = window * sinc;
integrate_impulse += value/16;
integrate_step += integrate_impulse/16;
}
step[i] = (float) integrate_impulse;
ramp[i] = (float) integrate_step;
for (j=0; j < 16; ++j) {
float sinc_t = 3.141592f* (i-half) / oversample;
float sinc = (i==half) ? 1.0f : (float) sin(sinc_t) / (sinc_t);
float wt = 2.0f*3.1415926f * i / (n-1);
float window = (float) (0.355768 - 0.487396*cos(wt) + 0.144232*cos(2*wt) - 0.012604*cos(3*wt)); // Nuttall
double value = window * sinc;
integrate_impulse += value/16;
integrate_step += integrate_impulse/16;
}
step[i] = (float) integrate_impulse;
ramp[i] = (float) integrate_step;
}
// renormalize
for (i=0; i < n; ++i) {
step[i] = step[i] * (float) (1.0 / step[n-1]); // step needs to reach to 1.0
ramp[i] = ramp[i] * (float) (halfwidth / ramp[n-1]); // ramp needs to become a slope of 1.0 after oversampling
step[i] = step[i] * (float) (1.0 / step[n-1]); // step needs to reach to 1.0
ramp[i] = ramp[i] * (float) (halfwidth / ramp[n-1]); // ramp needs to become a slope of 1.0 after oversampling
}
// deinterleave to allow efficient interpolation e.g. w/SIMD
for (j=0; j <= oversample; ++j) {
for (i=0; i < width; ++i) {
blep_buffer [j*width+i] = step[j+i*oversample];
blamp_buffer[j*width+i] = ramp[j+i*oversample];
}
for (i=0; i < width; ++i) {
blep_buffer [j*width+i] = step[j+i*oversample];
blamp_buffer[j*width+i] = ramp[j+i*oversample];
}
}
// subtract out the naive waveform; note we can't do this to the raw data
// above, because we want the discontinuity to be in a different locations
// for j=0 and j=oversample (which exists to provide something to interpolate against)
for (j=0; j <= oversample; ++j) {
// subtract step
for (i=halfwidth; i < width; ++i)
blep_buffer [j*width+i] -= 1.0f;
// subtract ramp
for (i=halfwidth; i < width; ++i)
blamp_buffer[j*width+i] -= (j+i*oversample-half)*(1.0f/oversample);
// subtract step
for (i=halfwidth; i < width; ++i)
blep_buffer [j*width+i] -= 1.0f;
// subtract ramp
for (i=halfwidth; i < width; ++i)
blamp_buffer[j*width+i] -= (j+i*oversample-half)*(1.0f/oversample);
}
hexblep.blep = blep_buffer;
......@@ -630,10 +628,10 @@ STB_HEXWAVE_DEF void hexwave_init(int width, int oversample, float *user_buffer)
hexblep.width = width;
hexblep.oversample = oversample;
#ifndef STB_HEXWAVE_NO_ALLOCATION
#ifndef STB_HEXWAVE_NO_ALLOCATION
if (user_buffer == 0)
free(buffers);
#endif
free(buffers);
#endif
}
#endif // STB_HEXWAVE_IMPLEMENTATION
......
source diff could not be displayed: it is too large. Options to address this: view the blob.