Open source Star Ruler 2 source code!

This commit is contained in:
Lucas de Vries
2018-07-17 14:15:37 +02:00
commit cc307720ff
4342 changed files with 2365070 additions and 0 deletions
+261
View File
@@ -0,0 +1,261 @@
#include "bmf_loader.h"
#include <fstream>
#include <map>
#include <string.h>
//BMF Specification (version 0):
//uint32 == "BMF "
//uint32: version number (0 for current version)
//
//uint32: vertex count
//Repeat <vertex count>:
// float x,y,z: vertex[n] coordinates
//
//uint32: normal count
//Repeat <normal count>:
// float x,y,z: normal[n] values
//
//uint32: uv count
//Repeat <uv count>:
// float u,v: uv[n] coordinates
//
//uint32: face count
//Repeat <face count>:
// If <vertex count> <= 0xffff
// uint16 v1,v2,v3: face[n] vertex indices
// Else
// uint32 v1,v2,v3: face[n] vertex indices
//
// If <normal count> > 0
// If <normal count> <= 0xffff
// uint16 n1,n2,n3: face[n] normal indices
// Else
// uint32 n1,n2,n3: face[n] normal indices
//
// If <uv count> > 0
// If <uv count> <= 0xffff
// uint16 u1,u2,u3: face[n] uv indices
// Else
// uint32 u1,u2,u3: face[n] uv indices
//First 4 characters of any Binary Mesh File
const char* bmfHead = "BMF ";
namespace render {
struct UV {
float u, v;
};
struct VertexIndex {
unsigned a, b, c;
VertexIndex() : a(0), b(0), c(0) {}
VertexIndex(unsigned A, unsigned B, unsigned C) : a(A), b(B), c(C) {}
bool operator<(const VertexIndex& other) const {
return memcmp(this, &other, sizeof(unsigned) * 3) < 0;
}
};
void loadBinaryMesh(const char* filename, Mesh& mesh) {
static_assert(sizeof(vec3f) == 12, "vec3f must be the size of 3 floats");
static_assert(sizeof(UV) == 8, "UV must be the size of 2 floats");
std::vector<vec3f> vertices;
std::vector<vec3f> normals;
std::vector<UV> uvs;
std::ifstream file(filename, std::ios_base::binary | std::ios_base::in);
if(!file.is_open())
return;
char buff[4];
file.read(buff, 4);
if(file.fail() || strncmp(bmfHead, buff, 4) != 0)
return;
unsigned version;
file.read((char*)&version, sizeof(version));
if(file.fail() || version != 0)
return;
//Vertices
unsigned count;
file.read((char*)&count, sizeof(count));
if(file.fail() || count == 0)
return;
vertices.resize(count);
file.read((char*)&vertices.front(), sizeof(vec3f) * count);
//Normals
file.read((char*)&count, sizeof(count));
if(file.fail())
return;
normals.resize(count);
file.read((char*)&normals.front(), sizeof(vec3f) * count);
//UVs
file.read((char*)&count, sizeof(count));
if(file.fail())
return;
uvs.resize(count);
file.read((char*)&uvs.front(), sizeof(UV) * count);
//Faces
file.read((char*)&count, sizeof(count));
if(file.fail())
return;
std::map<VertexIndex,unsigned> vertexMap;
mesh.faces.reserve(count);
mesh.vertices.reserve(count);
for(unsigned i = 0; i < count; ++i) {
Mesh::Face face;
Vertex vertex[3];
VertexIndex vertIndices[3];
//Load vertex position indices
for(unsigned j = 0; j < 3; ++j) {
unsigned index;
if(vertices.size() <= 0xffff) {
unsigned short v;
file.read((char*)&v, sizeof(v));
index = v;
}
else {
file.read((char*)&index, sizeof(index));
}
if(index >= vertices.size())
index = 0;
vertex[j].position = vertices[index];
vertIndices[j].a = index;
}
//Load vertex normal indices
if(!normals.empty()) {
for(unsigned j = 0; j < 3; ++j) {
unsigned index;
if(normals.size() <= 0xffff) {
unsigned short v;
file.read((char*)&v, sizeof(v));
index = v;
}
else {
file.read((char*)&index, sizeof(index));
}
if(index >= normals.size())
index = 0;
vertex[j].normal = normals[index];
vertIndices[j].b = index;
}
}
//Load vertex uv indices
if(!uvs.empty()) {
for(unsigned j = 0; j < 3; ++j) {
unsigned index;
if(uvs.size() <= 0xffff) {
unsigned short v;
file.read((char*)&v, sizeof(v));
index = v;
}
else {
file.read((char*)&index, sizeof(index));
}
if(index >= uvs.size())
index = 0;
vertex[j].u = uvs[index].u;
vertex[j].v = uvs[index].v;
vertIndices[j].c = index;
}
}
if(file.fail())
return;
//Automatically fuse identical vertices and store results in mesh
unsigned indices[3];
for(unsigned j = 0; j < 3; ++j) {
auto previous = vertexMap.find(vertIndices[j]);
if(previous != vertexMap.end()) {
indices[j] = previous->second;
}
else {
indices[j] = (unsigned)mesh.vertices.size();
mesh.vertices.push_back(vertex[j]);
vertexMap[vertIndices[j]] = indices[j];
}
}
face.a = indices[0];
face.b = indices[1];
face.c = indices[2];
mesh.faces.push_back(face);
}
}
bool saveBinaryMesh(const char* filename, Mesh& mesh) {
std::ofstream file(filename, std::ios_base::binary | std::ios_base::out);
if(!file.is_open())
return false;
file.write(bmfHead, 4);
unsigned version = 0;
file.write((char*)&version, sizeof(version));
//TODO: Writes duplicate data unnecessarily
unsigned count;
count = (unsigned)mesh.vertices.size();
file.write((char*)&count, sizeof(count));
for(unsigned i = 0; i < count; ++i)
file.write((char*)&mesh.vertices[i].position, sizeof(vec3f));
count = (unsigned)mesh.vertices.size();
file.write((char*)&count, sizeof(count));
for(unsigned i = 0; i < count; ++i)
file.write((char*)&mesh.vertices[i].normal, sizeof(vec3f));
count = (unsigned)mesh.vertices.size();
file.write((char*)&count, sizeof(count));
for(unsigned i = 0; i < count; ++i) {
file.write((char*)&mesh.vertices[i].u, sizeof(float));
file.write((char*)&mesh.vertices[i].v, sizeof(float));
}
bool shortIndices = mesh.vertices.size() <= 0xffff;
count = (unsigned)mesh.faces.size();
file.write((char*)&count, sizeof(count));
for(unsigned i = 0; i < count; ++i) {
Mesh::Face face = mesh.faces[i];
if(shortIndices) {
unsigned short data[] = {face.a, face.b, face.c, face.a, face.b, face.c, face.a, face.b, face.c};
file.write((char*)data, sizeof(data));
}
else {
unsigned data[] = {face.a, face.b, face.c, face.a, face.b, face.c, face.a, face.b, face.c};
file.write((char*)data, sizeof(data));
}
}
return true;
}
};
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include "mesh.h"
namespace render {
void loadBinaryMesh(const char* filename, Mesh& mesh);
bool saveBinaryMesh(const char* filename, Mesh& mesh);
};
+409
View File
@@ -0,0 +1,409 @@
#include "render/camera.h"
#include "constants.h"
#include <stdio.h>
namespace render {
const double pctPerSecond = 0.9999;
Camera::Camera()
: radius(300), qd_yaw(0), qd_pitch(0), qd_roll(0), qd_zoom(1), qd_zoom_min_distance(0), positionBound(vec3d(-1e7), vec3d(1e7)), maxDist(1e7),
qd_abs_yaw(0), qd_abs_pitch(0), zNear(1), zFar(1000), fov(0.8), aspect(1), objectCamera(false), linearZoom(false), lockedRotation(true)
{
}
void Camera::yaw(double radians, bool snap) {
if(snap)
rotation = rotation * quaterniond::fromAxisAngle(vec3d::up(), radians);
else
qd_yaw += radians;
}
void Camera::pitch(double radians, bool snap) {
if(snap) {
rotation = rotation * quaterniond::fromAxisAngle(vec3d::right(), radians);
rotation.normalize();
}
else {
qd_pitch += radians;
}
}
void Camera::abs_yaw(double radians, bool snap) {
if(snap) {
rotation = rotation * quaterniond::fromAxisAngle(rotation.inverted() * vec3d::up(), radians);
rotation.normalize();
}
else {
qd_abs_yaw += radians;
}
}
void Camera::abs_yaw_to(double radians, bool snap) {
if(snap) {
double amount = getYaw() - radians;
rotation = rotation * quaterniond::fromAxisAngle(rotation.inverted() * vec3d::up(), amount);
}
else
qd_abs_yaw = getYaw() - radians;
}
void Camera::abs_pitch(double radians, bool snap) {
if(snap) {
rotation = rotation * quaterniond::fromAxisAngle(rotation.inverted() * vec3d::right(), radians);
rotation.normalize();
}
else {
qd_abs_pitch += radians;
}
}
void Camera::abs_pitch_to(double radians, bool snap) {
if(snap) {
double amount = getPitch() - radians;
rotation = rotation * quaterniond::fromAxisAngle(rotation.inverted() * vec3d::right(), amount);
}
else {
qd_abs_pitch = getPitch() - radians;
}
}
void Camera::roll(double radians, bool snap) {
if(snap)
rotation = rotation * quaterniond::fromAxisAngle(vec3d::front(), radians);
else
qd_roll += radians;
}
void Camera::zoom(double factor) {
qd_zoom *= factor;
qd_zoom_point = vec3d();
qd_zoom_min_distance = 0;
}
void Camera::zoomTo(double factor, const vec3d& towards, double minDistance) {
qd_zoom *= factor;
qd_zoom_point = towards;
qd_zoom_line = vec3d();
qd_zoom_min_distance = minDistance;
}
void Camera::zoomAlong(double factor, const vec3d& line) {
qd_zoom *= factor;
qd_zoom_line = line;
qd_zoom_point = vec3d();
qd_zoom_min_distance = 0;
}
void Camera::setRadius(double amount) {
radius = amount;
if(radius > maxDist)
radius = maxDist;
if(radius < 0)
radius = 1.0;
}
double Camera::getRadius() {
return radius;
}
void Camera::move_world(const vec3d& motion) {
qd_world_motion += motion * radius;
}
void Camera::move_world_abs(const vec3d& motion) {
qd_world_motion += motion;
}
void Camera::move_cam(const vec3d& motion) {
qd_cam_motion += motion * radius;
}
void Camera::move_cam_abs(const vec3d& motion) {
qd_cam_motion += motion;
}
void Camera::move_abs(const vec3d& motion) {
qd_abs_motion += motion;
}
void Camera::setPositionBound(const vec3d& minimum, const vec3d& maximum) {
positionBound = AABBoxd(minimum, maximum);
}
void Camera::setMaxDistance(double dist) {
maxDist = dist;
}
void Camera::setRenderConstraints(double ZNear, double ZFar, double FOV, double Aspect, double w, double h) {
zNear = ZNear;
zFar = ZFar;
fov = FOV * (twopi / 360.0);
aspect = Aspect;
pxWidth = w;
pxHeight = h;
}
bool Camera::inverted() {
return getUp().y <= 0;
}
vec3d Camera::getPosition() const {
if(objectCamera)
return center;
else
return center + (rotation * vec3d::front(-radius));
}
vec3d Camera::getMovedPosition(vec3d pos, double pct) const {
//Camera motion
if(qd_cam_motion.getLength() > 0.00001) {
vec3d cam_x = rotation * vec3d::right(); cam_x.normalize();
vec3d cam_y = rotation * vec3d::up(); cam_y.normalize();
vec3d cam_z = rotation * vec3d::front(); cam_z.normalize();
pos += (cam_x * qd_cam_motion.x * pct) + (cam_y * qd_cam_motion.y * pct) + (cam_z * qd_cam_motion.z * pct);
}
//World motion
if(qd_world_motion.getLength() > 0.00001) {
vec3d world_x = rotation * vec3d::right(); world_x.y = 0; world_x.normalize();
vec3d world_y = rotation * vec3d::up(); world_y.x = 0; world_y.z = 0; world_y.normalize();
vec3d world_z = rotation * vec3d::front(); world_z.y = 0; world_z.normalize();
pos += (world_x * qd_world_motion.x * pct) + (world_y * qd_world_motion.y * pct) + (world_z * qd_world_motion.z * pct);
}
//Absolute motion
if(qd_abs_motion.getLength() > 0.00001)
pos += qd_abs_motion * pct;
pos = pos.elementMax(positionBound.minimum).elementMin(positionBound.maximum);
return pos;
}
vec3d Camera::getFinalPosition() const {
return getMovedPosition(getPosition(), 1.0);
}
vec3d Camera::getFacing() const {
return rotation * vec3d::front();
}
vec3d Camera::getRight() const {
return rotation * vec3d::right();
}
vec3d Camera::getUp() const {
return (rotation * vec3d::up()).normalized();
}
quaterniond Camera::getRotation() const {
return rotation;
}
vec3d Camera::getLookAt() const {
return center;
}
vec3d Camera::getFinalLookAt() const {
return getMovedPosition(getLookAt(), 1.0);
}
double Camera::getDistance() const {
return radius;
}
double Camera::getYaw() const {
vec3d rotated = rotation * vec3d::front();
return atan2(rotated.z, rotated.x);
}
double Camera::getPitch() const {
return 0.0;
}
double Camera::getRoll() const {
return 0.0;
}
vec2i Camera::screenPos(const vec3d& point) const {
//Transform to camera space
vec3d transformed = rotation.inverted() * (point - getPosition());
//Transform to OpenGL's space
vec3d converted = vec3d(vec3d::right().dot(transformed), vec3d::up().dot(transformed), -vec3d::front().dot(transformed));
Matrix projection = Matrix::projection(fov / (twopi / 360.0), aspect, zNear, zFar);
//Apply projection and return final result (Left-Right and Top-Bottom are x[-1,1] and y[-1,1])
vec4d pos = projection * vec4d(converted.x, converted.y, converted.z, 1.0);
pos.x /= -pos.w; pos.y /= -pos.w;
return vec2i((int)(pxWidth * (pos.x + 1.0) * 0.5), (int)(pxHeight * (pos.y + 1.0) * 0.5));
}
double Camera::screenAngle(const vec3d& toPoint) const {
quaterniond inv = rotation.inverted();
vec3d cam = inv * center;
vec3d pos = inv * toPoint;
vec2d flatOffset(cam.x - pos.x, pos.y - cam.y);
return flatOffset.radians() / pi;
}
line3dd Camera::screenToRay(double x, double y) const {
double tan_fov = tan(fov/2.0);
vec3d view_dir =
vec3d::front(1.0)
+ vec3d::right(tan_fov * 2.0 * (0.5-x) * aspect)
+ vec3d::up(tan_fov * 2.0 * (0.5-y));
view_dir = rotation * view_dir.normalized();
vec3d start = view_dir * zNear, end = view_dir * zFar;
vec3d camPos = getPosition();
start += camPos; end += camPos;
return line3dd(start, end);
}
void Camera::setLockedRotation(bool locked) {
lockedRotation = locked;
}
void Camera::animatePercentage(double pct) {
{ //Rotation
auto prevRot = rotation;
auto rotLock = [&prevRot,this]() {
if(lockedRotation && (rotation * vec3d::up()).y < 0.001)
rotation = prevRot;
else
prevRot = rotation;
};
if(fabs(qd_abs_pitch) > 0.00001)
rotation = rotation * quaterniond::fromAxisAngle(rotation.inverted() * vec3d::right(), pct * qd_abs_pitch);
qd_abs_pitch *= 1.0 - pct;
rotLock();
if(fabs(qd_pitch) > 0.00001)
rotation = rotation * quaterniond::fromAxisAngle(vec3d::right(), pct * qd_pitch);
qd_pitch *= 1.0 - pct;
rotLock();
if(fabs(qd_roll) > 0.00001)
rotation = rotation * quaterniond::fromAxisAngle(vec3d::front(), pct * qd_roll);
qd_roll *= 1.0 - pct;
rotLock();
//Quaternion rotations
if(fabs(qd_yaw) > 0.00001)
rotation = rotation * quaterniond::fromAxisAngle(vec3d::up(), pct * qd_yaw);
qd_yaw *= 1.0 - pct;
//Absolute rotations around world axes
if(fabs(qd_abs_yaw) > 0.00001)
rotation = rotation * quaterniond::fromAxisAngle(rotation.inverted() * vec3d::up(), pct * qd_abs_yaw);
qd_abs_yaw *= 1.0 - pct;
rotation.normalize();
}
center = getMovedPosition(center, pct);
//Move the center of the camera
qd_cam_motion *= 1.0 - pct;
qd_world_motion *= 1.0 - pct;
qd_abs_motion *= 1.0 - pct;
//Zoom level
if(fabs(qd_zoom-1) > 0.00001) {
if(linearZoom) {
double dest = (qd_zoom * radius);
double zoomDist = dest - radius;
radius += pct * zoomDist;
qd_zoom = dest / radius;
}
else {
double doZoom = pow(qd_zoom, pct);
qd_zoom /= doZoom;
radius *= doZoom;
if(!qd_zoom_line.zero()) {
//Screw you and your entire 3D vector family
}
else if(!qd_zoom_point.zero()) {
center = qd_zoom_point.interpolate(center, doZoom);
}
}
if(radius > maxDist)
radius = maxDist;
if(radius < qd_zoom_min_distance)
radius = qd_zoom_min_distance;
}
else {
qd_zoom = 1;
}
}
void Camera::animate(double seconds) {
//Interpolate the camera's current state to its destination state
if(seconds <= 0)
return;
double pct = 1.0 - pow(1.0 - pctPerSecond,seconds);
animatePercentage(pct);
}
void Camera::snap() {
animatePercentage(1.0);
}
void Camera::snapTranslation() {
center = getMovedPosition(center, 1.0);
//Move the center of the camera
qd_cam_motion *= 0.0;
qd_world_motion *= 0.0;
qd_abs_motion *= 0.0;
}
void Camera::toLookAt(vec3d& cameraPosition, vec3d& cameraLookAt, vec3d& cameraUp) const {
cameraPosition = getPosition();
cameraLookAt = cameraPosition + getFacing();
cameraUp = getUp();
}
void Camera::setObjectCamera(bool ObjectCamera) {
objectCamera = ObjectCamera;
}
void Camera::setLinearZoom(bool linear) {
linearZoom = linear;
}
void Camera::resetRotation() {
//TODO: Make this calculate the required yaw/pitch/roll to
//reset back to defaults, so that this can be animated instead
//of always needing to snap.
rotation = quaterniond();
qd_yaw = 0;
qd_pitch = 0;
qd_roll = 0;
qd_zoom = 1;
}
void Camera::resetZoom() {
qd_zoom = 1;
radius = 300;
}
};
+109
View File
@@ -0,0 +1,109 @@
#pragma once
#include "quaternion.h"
#include "vec2.h"
#include "line3d.h"
#include "util/refcount.h"
#include "aabbox.h"
namespace render {
class Camera : public AtomicRefCounted {
private:
quaterniond rotation;
vec3d center;
double radius;
AABBoxd positionBound;
double maxDist;
vec3d qd_zoom_point;
vec3d qd_zoom_line;
vec3d qd_world_motion;
vec3d qd_cam_motion;
vec3d qd_abs_motion;
double qd_yaw, qd_pitch, qd_roll, qd_zoom;
double qd_abs_yaw, qd_abs_pitch;
double qd_zoom_min_distance;
double zNear, zFar, fov, aspect, pxWidth, pxHeight;
bool objectCamera;
bool linearZoom;
bool lockedRotation;
public:
void yaw(double radians, bool snap = false);
void pitch(double radians, bool snap = false);
void abs_yaw(double radians, bool snap = false);
void abs_pitch(double radians, bool snap = false);
void roll(double radians, bool snap = false);
void zoom(double factor);
void snap();
void snapTranslation();
void setRadius(double radius);
double getRadius();
void abs_yaw_to(double yaw, bool snap = false);
void abs_pitch_to(double pitch, bool snap = false);
void move_cam(const vec3d& motion);
void move_cam_abs(const vec3d& motion);
void move_world(const vec3d& motion);
void move_world_abs(const vec3d& motion);
void move_abs(const vec3d& motion);
void zoomTo(double factor, const vec3d& towards, double minDistance = 0);
void zoomAlong(double factor, const vec3d& line);
void animatePercentage(double pct);
void animate(double seconds);
void setLinearZoom(bool linear);
void setLockedRotation(bool locked);
void setPositionBound(const vec3d& minimum, const vec3d& maximum);
void setMaxDistance(double dist);
//Returns the angle in physical screen space
//that a particular point is directed in from the
//center of the screen
double screenAngle(const vec3d& toPoint) const;
vec2i screenPos(const vec3d& point) const;
//Returns a ray starting at the near Z plane, ending at the
//far Z plane, with each endpoint being at <x,y> on the screen in
//normalized coordinates [0,1]
line3dd screenToRay(double x, double y) const;
vec3d getMovedPosition(vec3d pos, double pct) const;
vec3d getFinalPosition() const;
vec3d getPosition() const;
vec3d getFacing() const;
vec3d getUp() const;
vec3d getRight() const;
quaterniond getRotation() const;
vec3d getLookAt() const;
vec3d getFinalLookAt() const;
double getDistance() const;
double getYaw() const;
double getPitch() const;
double getRoll() const;
//Returns whether the camera is currently upside down
bool inverted();
void setObjectCamera(bool ObjectCamera);
void toLookAt(vec3d& cameraPosition, vec3d& cameraLookAt, vec3d& cameraUp) const;
void setRenderConstraints(double ZNear, double ZFar, double FOV_degrees, double Aspect, double w, double h);
void resetRotation();
void resetZoom();
Camera();
};
};
+164
View File
@@ -0,0 +1,164 @@
#pragma once
#include "scene/node.h"
#include "render/render_state.h"
#include "render/render_mesh.h"
#include "render/texture.h"
#include "matrix.h"
#include "mesh.h"
#include "rect.h"
#include "image.h"
#include "color.h"
#include "vec2.h"
#include "line3d.h"
struct frustum;
namespace scene {
class Node;
};
namespace render {
enum PrimitiveType {
PT_Lines,
PT_LineStrip,
PT_Triangles,
PT_Quads,
};
class Camera;
class RenderDriver {
public:
RenderState activeRenderState;
scene::Node rootNode;
vec3d cam_pos, cam_facing, cam_up;
virtual bool init() = 0;
virtual void reportErrors(const char* context = nullptr) const = 0;
virtual const RenderState* getLastRenderState() const = 0;
virtual void setSkybox(const RenderState* mat) = 0;
virtual void setSkyboxMesh(const RenderMesh* mesh) = 0;
virtual void setScreenSize(int w, int h) = 0;
virtual void setFOV(double fov) = 0;
virtual void setNearFarPlanes(double near, double far) = 0;
virtual const frustum& getViewFrustum() const = 0;
virtual void setCameraData(Camera& camera) = 0;
virtual void setDefaultRenderState() = 0;
virtual void set2DRenderState() = 0;
virtual void switchToRenderState(const RenderState&) = 0;
virtual void setTransformation(const Matrix&) = 0;
virtual void setTransformationAbs(const Matrix&) = 0;
virtual void setTransformationIdentity() = 0;
virtual void setBBTransform(vec3d pos, double width, double rot) = 0;
virtual void resetTransformation() = 0;
virtual void getInverseView(float* mat3) const = 0;
virtual void getBillboardVecs(vec3d& upLeft, vec3d& upRight, double rotation = 0) const = 0;
virtual void getBillboardVecs(const vec3d& from, vec3d& upLeft, vec3d& upRight, double rotation = 0) const = 0;
virtual void clearRenderPrepared() = 0;
virtual bool isRenderPrepared() = 0;
virtual void prepareRender3D(Camera& camera, const recti* clip = 0) = 0;
virtual void prepareRender2D() = 0;
virtual void renderWorld() = 0;
virtual RenderMesh* createMesh(const Mesh& mesh) = 0;
virtual Shader* createShader() = 0;
virtual ShaderProgram* createShaderProgram(const char* vertex_shader, const char* fragment_shader) = 0;
virtual Texture* createTexture(Image& image, bool mipmap = true, bool cachePixels = false) = 0;
static Texture* createTexture();
static Texture* createCubemap();
virtual Texture* createRenderTarget(const vec2i& size) = 0;
virtual void setRenderTarget(Texture* texture, bool intermediate = false) = 0;
virtual Image* getScreen(int x, int y, int w, int h) = 0;
virtual void drawFPSGraph(const recti& location) = 0;
virtual void drawBillboard(vec3d center, double width) = 0;
virtual void drawLine(line3dd line, Color start, Color end) = 0;
virtual void drawQuad(
const RenderState* mat,
const vec2<float>* vertices,
const vec2<float>* textureCoords = 0,
const Color* color = 0
) = 0;
virtual void drawQuad(
const vec2<float>* vertices,
const vec2<float>* textureCoords,
const Color* colors
) = 0;
virtual void drawQuad(
const vec3d* vertices,
const vec2<float>* textureCoords,
const Color* colors
) = 0;
virtual void drawRectangle(
const recti& rectangle,
const RenderState* mat,
Color color,
const recti* clip = 0
) = 0;
virtual void drawRectangle(
recti rectangle,
const RenderState* mat = 0,
const recti* sourceRect = 0,
const Color* color = 0,
const recti* clip = 0
) = 0;
virtual void drawRectangle(
recti rectangle,
const RenderState* mat,
const recti* sourceRect,
const Color* color,
const recti* clip,
double rotation
) = 0;
virtual void drawBillboard(
vec3d center,
double width,
const RenderState& mat,
double rotation,
Color* color = 0
) = 0;
virtual void drawBillboard(
vec3d center,
double width,
const RenderState& mat,
const recti& source,
Color* color = 0
) = 0;
virtual void drawBillboard(
vec3d center,
double width,
const RenderState& mat,
const recti& source,
double rotation,
Color color = Color()
) = 0;
virtual void pushScreenClip(const recti& box) = 0;
virtual void popScreenClip() = 0;
virtual ~RenderDriver() {}
};
};
+62
View File
@@ -0,0 +1,62 @@
#pragma once
#include "render/texture.h"
#include "render/render_state.h"
#include "render/driver.h"
#include "color.h"
#include "image.h"
#include <vector>
namespace render {
typedef int fontChar;
class Font {
//Prevent copying the font
Font(const Font& other) {}
void operator=(const Font& other) {}
protected:
Font() : bold(0), italic(0) {}
public:
Font* bold;
Font* italic;
virtual vec2i getDimension(const char* text) const {
return vec2i();
};
virtual vec2i getDimension(int c, int lastC) const {
return vec2i();
}
virtual unsigned getBaseline() const {
return 0;
}
virtual unsigned getLineHeight() const {
return 0;
}
virtual void draw(render::RenderDriver* driver, const char* text,
int x, int y, const Color* color = 0, const recti* clip = 0) const {
};
virtual vec2i drawChar(render::RenderDriver* driver, int c, int lastC,
int X, int Y, const Color* color = 0, const recti* clip = 0) const {
return vec2i();
}
virtual ~Font() {
};
static Font* createDummyFont();
};
bool clipQuad(rectf pos, rectf source, const rectf* clip, vec2f* verts, vec2f* texcoords);
Font* loadFontFNT(render::RenderDriver* driver, const char* filename);
Font* loadFontFT2(render::RenderDriver& driver, const char* filename,
std::vector<std::pair<int,int>>& pages, int size);
};
+368
View File
@@ -0,0 +1,368 @@
#include "render/font.h"
#include "render/driver.h"
#include <stdio.h>
#include "str_util.h"
#include "vec2.h"
#include "main/initialization.h"
#include <algorithm>
namespace resource {
extern render::Texture* queueImage(const std::string& abs_file, int priority, bool mipmap, bool cachePixels, bool cubemap = false);
};
namespace render {
class FontFNT : public Font {
public:
std::vector<render::RenderState*> textures;
struct Glyph {
unsigned short x : 16, y : 16;
bool draw : 1;
unsigned char page : 3;
unsigned char w : 8, h : 8;
char wOff : 8, hOff : 8;
unsigned char xAdv : 8;
};
struct GlyphEntry {
fontChar letter;
Glyph glyph;
bool operator<(const GlyphEntry& other) const { return letter < other.letter; }
GlyphEntry(fontChar Letter) : letter(Letter) {}
GlyphEntry(fontChar Letter, Glyph& Glyph) : letter(Letter), glyph(Glyph) {}
};
struct KernEntry {
union {
unsigned v;
struct { fontChar left : 16, right : 16; };
};
int offset;
bool operator<(const KernEntry& other) const { return v < other.v; }
KernEntry(fontChar Left, fontChar Right) : left(Left), right(Right) {}
};
Glyph* lowChars;
std::vector<GlyphEntry> hiChars;
std::vector<KernEntry> kerning;
unsigned glyphHeight, textureHeight, textureWidth;
unsigned getBaseline() const {
return glyphHeight;
}
unsigned getLineHeight() const {
return glyphHeight;
}
void draw(render::RenderDriver* driver, const char* text, int X, int Y, const Color* color, const recti* clip = 0) const {
int x = X, y = Y;
Color colors[4];
if(color)
colors[0] = colors[1] = colors[2] = colors[3] = *color;
float xFactor = 1.f / float(textureWidth), yFactor = 1.f / float(textureHeight);
vec2f font_verts[4], font_tc[4];
rectf fclip;
if(clip)
fclip = rectf(*clip);
unsigned lastTexture = (unsigned)textures.size();
fontChar lastC = 0;
u8it it(text);
while(fontChar c = it++) {
if(c == L'\n') {
y += glyphHeight;
x = X;
lastC = 0;
}
else {
if(lastC) {
auto k = std::lower_bound(kerning.begin(), kerning.end(), KernEntry(lastC, c));
if(k != kerning.end() && k->left == lastC && k->right == c)
x += k->offset;
}
lastC = c;
Glyph glyph;
if(c <= 0xff)
glyph = lowChars[c];
else {
auto g = std::lower_bound(hiChars.begin(), hiChars.end(), c);
if(g != hiChars.end() && g->letter == c) {
glyph = g->glyph;
}
else {
glyph = lowChars['?'];
}
}
if(glyph.draw) {
rectf pos = rectf::area(vec2f((float)(x + glyph.wOff), (float)(y + glyph.hOff)), vec2f(glyph.w, glyph.h));
rectf source = rectf::area(vec2f((float)glyph.x * xFactor, (float)glyph.y * yFactor),
vec2f((float)glyph.w * xFactor, (float)glyph.h * yFactor));
if(clipQuad(pos, source, clip ? &fclip : 0, font_verts, font_tc)) {
if(glyph.page != lastTexture) {
auto& mat = *textures[glyph.page];
driver->switchToRenderState(mat);
lastTexture = glyph.page;
}
driver->drawQuad(font_verts,font_tc,color ? colors : 0);
}
}
x += glyph.xAdv;
}
}
}
vec2i drawChar(render::RenderDriver* driver, int c, int lastC,
int X, int Y, const Color* color, const recti* clip = 0) const {
int x = X, y = Y;
Color colors[4];
if(color)
colors[0] = colors[1] = colors[2] = colors[3] = *color;
float xFactor = 1.f / float(textureWidth), yFactor = 1.f / float(textureHeight);
vec2f font_verts[4], font_tc[4];
rectf fclip;
if(clip)
fclip = rectf(*clip);
if(lastC) {
auto k = std::lower_bound(kerning.begin(), kerning.end(), KernEntry(lastC, c));
if(k != kerning.end() && k->left == lastC && k->right == c)
x += k->offset;
}
Glyph glyph;
if(c <= 0xff)
glyph = lowChars[c];
else {
auto g = std::lower_bound(hiChars.begin(), hiChars.end(), c);
if(g != hiChars.end() && g->letter == c) {
glyph = g->glyph;
}
else {
glyph = lowChars['?'];
}
}
if(glyph.draw) {
rectf pos = rectf::area(vec2f((float)(x + glyph.wOff), (float)(y + glyph.hOff)), vec2f(glyph.w, glyph.h));
rectf source = rectf::area(vec2f((float)glyph.x * xFactor, (float)glyph.y * yFactor),
vec2f((float)glyph.w * xFactor, (float)glyph.h * yFactor));
if(clipQuad(pos, source, clip ? &fclip : 0, font_verts, font_tc)) {
auto& mat = *textures[glyph.page];
driver->switchToRenderState(mat);
driver->drawQuad(font_verts,font_tc,color ? colors : 0);
}
}
x += glyph.xAdv;
return vec2i(x - X, y - Y);
}
vec2i getDimension(const char* text) const {
vec2i size(0, glyphHeight);
fontChar lastC = 0;
u8it it(text);
while(fontChar c = it++) {
if(lastC) {
auto k = std::lower_bound(kerning.begin(), kerning.end(), KernEntry(lastC, c));
if(k != kerning.end() && k->left == lastC && k->right == c)
size.x += k->offset;
}
lastC = c;
const Glyph* glyph = 0;
if(c <= 0xff) {
glyph = &lowChars[c];
}
else {
auto g = std::lower_bound(hiChars.begin(), hiChars.end(), c);
if(g != hiChars.end() && g->letter == c)
glyph = &g->glyph;
else
glyph = &lowChars['?'];
}
if(glyph)
size.x += glyph->xAdv;
++text;
}
return size;
}
vec2i getDimension(int c, int lastC) const {
vec2i size(0, glyphHeight);
if(lastC) {
auto k = std::lower_bound(kerning.begin(), kerning.end(), KernEntry(lastC, c));
if(k != kerning.end() && k->left == lastC && k->right == c)
size.x += k->offset;
}
const Glyph* glyph = 0;
if(c <= 0xff) {
glyph = &lowChars[c];
}
else {
auto g = std::lower_bound(hiChars.begin(), hiChars.end(), c);
if(g != hiChars.end() && g->letter == c)
glyph = &g->glyph;
else
glyph = &lowChars['?'];
}
if(glyph)
size.x += glyph->xAdv;
return size;
}
FontFNT()
: lowChars(new Glyph[256]), glyphHeight(0), textureHeight(1), textureWidth(1)
{
memset(lowChars, 0, sizeof(Glyph) * 256);
}
~FontFNT()
{
delete[] lowChars;
foreach(tex, textures)
delete *tex;
}
};
Font* loadFontFNT(render::RenderDriver* driver, const char* filename) {
auto file = fopen(filename, "r");
if(file == 0)
return 0;
auto slash = strrchr(filename, '/');
if(auto bSlash = strrchr(filename, '\\'))
if(bSlash > slash)
slash = bSlash;
std::string folder(filename, slash ? slash + 1 - filename : 0);
//Skip first line
do {
int c = fgetc(file);
if(c == L'\n' || c == L'\r' || c == EOF)
break;
} while(true);
if(feof(file)) {
fclose(file);
return 0;
}
FontFNT* font = new FontFNT;
if(false) {
failedLoad:
delete font;
fclose(file);
return 0;
}
int pages, lineCheck;
if(fscanf(file,
" common lineHeight=%i base=%*i scaleW=%i scaleH=%i pages=%i packed=%*i alphaChnl=%*i redChnl=%*i greenChnl=%*i blueChnl=%i ",
&font->glyphHeight, &font->textureWidth, &font->textureHeight, &pages, &lineCheck) < 5
|| pages == 0 || pages > 7 || font->glyphHeight <= 0)
goto failedLoad;
int page; char pageName[256];
while(pages--) {
int args = fscanf(file,"page id=%i file=%255s ", &page, pageName);
if(args < 2 || (unsigned int)page != font->textures.size())
goto failedLoad;
std::string pageFile = folder + trim(pageName,"\"");
auto material = new render::RenderState();
material->depthTest = render::DT_NoDepthTest;
material->lighting = false;
material->culling = render::FC_None;
material->baseMat = render::MAT_Alpha;
if(load_resources)
material->textures[0] = resource::queueImage(pageFile, 20, true, false);
font->textures.push_back(material);
}
int glyphs;
if(fscanf(file, "chars count=%i ", &glyphs) != 1)
goto failedLoad;
while(glyphs--) {
int c, x, y, w, h, wOff, hOff, xAdv, page, channel;
if(fscanf(file, "char id=%i x=%i y=%i width=%i height=%i xoffset=%i yoffset=%i xadvance=%i page=%i chnl=%i ",
&c, &x, &y, &w, &h, &wOff, &hOff, &xAdv, &page, &channel) != 10
|| page < 0 || page >= (int)font->textures.size())
goto failedLoad;
FontFNT::Glyph glyph;
glyph.draw = c != 32;
glyph.x = x;
glyph.y = y;
glyph.w = w;
glyph.h = h;
glyph.wOff = wOff;
glyph.hOff = hOff;
glyph.xAdv = xAdv;
glyph.page = page;
if(c <= 0xff)
font->lowChars[c] = glyph;
else
font->hiChars.push_back(FontFNT::GlyphEntry(c,glyph));
}
//Load kerning data
int kerns;
if(fscanf(file, "kernings count=%i ", &kerns) == 1) {
while(kerns--) {
int left, right, offset;
if(fscanf(file, "kerning first=%i second=%i amount=%i ", &left, &right, &offset) == 3) {
FontFNT::KernEntry kerning((fontChar)left, (fontChar)right);
kerning.offset = offset;
font->kerning.push_back(kerning);
}
else {
break;
}
}
}
std::sort(font->hiChars.begin(), font->hiChars.end());
std::sort(font->kerning.begin(), font->kerning.end());
return font;
}
};
+487
View File
@@ -0,0 +1,487 @@
#include "render/font.h"
#include "render/driver.h"
#include "render/vertexBuffer.h"
#include "main/initialization.h"
#include <stdio.h>
#include "str_util.h"
#include "vec2.h"
#ifdef _MSC_VER
#include <ft2build.h>
#else
#include <freetype2/ft2build.h>
#endif
#include FT_FREETYPE_H
#include FT_SIZES_H
#include <map>
#include <algorithm>
namespace resource {
extern render::Texture* queueImage(Image* img, int priority, bool mipmap, bool cachePixels);
};
namespace render {
Font* Font::createDummyFont() {
return new Font();
}
bool clipQuad(rectf pos, rectf source, const rectf* clip, vec2f* verts, vec2f* texcoords) {
if(clip) {
if(!clip->overlaps(pos))
return false;
if(!clip->isRectInside(pos)) {
//TODThere seem to be some oddities with the texture coordinates if a clip occurs
rectf clipped = clip->clipAgainst(pos);
source = source.clipProportional(pos, clipped);
pos = clipped;
}
}
verts[0] = vec2f(pos.topLeft.x, pos.topLeft.y);
verts[1] = vec2f(pos.botRight.x, pos.topLeft.y);
verts[3] = vec2f(pos.topLeft.x, pos.botRight.y);
verts[2] = vec2f(pos.botRight.x, pos.botRight.y);
texcoords[0] = vec2f(source.topLeft.x, source.topLeft.y);
texcoords[1] = vec2f(source.botRight.x, source.topLeft.y);
texcoords[3] = vec2f(source.topLeft.x, source.botRight.y);
texcoords[2] = vec2f(source.botRight.x, source.botRight.y);
return true;
}
class FontFT2 : public Font {
public:
std::vector<render::RenderState*> textures;
struct Glyph {
unsigned short x : 16, y : 16;
bool draw : 1;
unsigned char page : 7;
unsigned char w : 8, h : 8;
char wOff : 8, hOff : 8;
float xAdv;
int glyph_index;
};
struct GlyphEntry {
fontChar letter;
Glyph glyph;
bool operator<(const GlyphEntry& other) const { return letter < other.letter; }
GlyphEntry(fontChar Letter) : letter(Letter) {}
GlyphEntry(fontChar Letter, Glyph& Glyph) : letter(Letter), glyph(Glyph) {}
};
Glyph* lowChars;
FT_Face* face;
FT_Size size;
std::vector<GlyphEntry> hiChars;
double x_scale, y_scale;
unsigned glyphHeight, baseLine, textureHeight, textureWidth;
void draw(render::RenderDriver* driver, const char* text, int X, int Y, const Color* color, const recti* clip = 0) const {
float x = (float)X, y = (float)Y;
Color col = color ? *color : Color();
float xFactor = 1.f / float(textureWidth), yFactor = 1.f / float(textureHeight);
vec2f font_verts[4], font_tc[4];
rectf fclip;
if(clip)
fclip = rectf(*clip);
FT_Activate_Size(size);
unsigned lastTexture = (unsigned)textures.size();
VertexBufferTCV* buffer = 0;
u8it it(text);
fontChar lastC = 0;
while(fontChar c = it++) {
if(c == L'\n') {
y += glyphHeight;
x = (float)X;
lastC = 0;
}
else if(c == L'\t') {
x += 32.f - fmod(x - X, 32.f);
lastC = L' ';
}
else {
Glyph glyph;
if(c <= 0xff)
glyph = lowChars[c];
else {
auto g = std::lower_bound(hiChars.begin(), hiChars.end(), c);
if(g != hiChars.end() && g->letter == c) {
glyph = g->glyph;
}
else {
glyph = lowChars['?'];
}
}
//Don't kern anything involving digits
if(c >= '0' && c <= '9') {
lastC = 0;
}
else {
if(lastC) {
FT_Vector kerning;
FT_Get_Kerning(*face, lastC, glyph.glyph_index, 0/*FT_KERNING_UNFITTED*/, &kerning);
if(kerning.x != 0)
x += ((float)kerning.x) / 64.f;
}
lastC = glyph.glyph_index;
}
if(glyph.draw) {
rectf pos = rectf::area(vec2f(x + (float)glyph.wOff, y + (float)glyph.hOff), vec2f(glyph.w, glyph.h));
rectf source = rectf::area(vec2f((float)glyph.x * xFactor, (float)glyph.y * yFactor),
vec2f((float)glyph.w * xFactor, (float)glyph.h * yFactor));
if(clipQuad(pos, source, clip ? &fclip : 0, font_verts, font_tc)) {
if(glyph.page != lastTexture) {
auto& mat = *textures[glyph.page];
buffer = VertexBufferTCV::fetch(&mat);
lastTexture = glyph.page;
}
auto* verts = buffer->request(1, PT_Quads);
for(unsigned i = 0; i < 4; ++i) {
auto& v = verts[i];
v.uv = font_tc[i];
v.col = col;
v.pos.set(font_verts[i].x, font_verts[i].y, 0);
}
}
}
x += glyph.xAdv;
}
}
}
vec2i drawChar(render::RenderDriver* driver, int c, int lastC,
int X, int Y, const Color* color, const recti* clip = 0) const {
float x = (float)X, y = (float)Y;
Color colors[4];
if(color)
colors[0] = colors[1] = colors[2] = colors[3] = *color;
rectf fclip;
if(clip)
fclip = rectf(*clip);
float xFactor = 1.f / float(textureWidth), yFactor = 1.f / float(textureHeight);
vec2f font_verts[4], font_tc[4];
FT_Activate_Size(size);
Glyph glyph;
if(c <= 0xff)
glyph = lowChars[c];
else {
auto g = std::lower_bound(hiChars.begin(), hiChars.end(), c);
if(g != hiChars.end() && g->letter == c) {
glyph = g->glyph;
}
else {
glyph = lowChars['?'];
}
}
//Don't kern anything involving digits
if(c >= '0' && c <= '9') {
lastC = 0;
}
else {
if(lastC) {
FT_Vector kerning;
FT_Get_Kerning(*face, lastC, glyph.glyph_index, 0, &kerning);
if(kerning.x != 0)
x += ((float)kerning.x) / 64.f;
}
lastC = glyph.glyph_index;
}
if(glyph.draw) {
rectf pos = rectf::area(vec2f(x + glyph.wOff, y + glyph.hOff), vec2f(glyph.w, glyph.h));
rectf source = rectf::area(vec2f((float)glyph.x * xFactor, (float)glyph.y * yFactor),
vec2f((float)glyph.w * xFactor, (float)glyph.h * yFactor));
if(clipQuad(pos, source, clip ? &fclip : 0, font_verts, font_tc))
driver->drawQuad(textures[glyph.page], font_verts, font_tc, color ? colors : 0);
}
x += glyph.xAdv;
return vec2i((int)x - X, (int)y - Y);
}
vec2i getDimension(const char* text) const {
vec2i dim(0, glyphHeight);
FT_Activate_Size(size);
fontChar lastC = 0;
u8it it(text);
unsigned maxWidth = 0;
while(fontChar c = it++) {
const Glyph* glyph = 0;
if(c == L'\n') {
if(dim.x > (int)maxWidth)
maxWidth = dim.x;
dim.x = 0;
dim.y += glyphHeight;
}
else if(c <= 0xff) {
glyph = &lowChars[c];
}
else {
auto g = std::lower_bound(hiChars.begin(), hiChars.end(), c);
if(g != hiChars.end() && g->letter == c)
glyph = &g->glyph;
else
glyph = &lowChars['?'];
}
if(glyph) {
dim.x += (int)glyph->xAdv;
//Don't kern anything involving digits
if(c < '0' || c > '9') {
if(lastC) {
FT_Vector kerning;
FT_Get_Kerning(*face, lastC, glyph->glyph_index, 0, &kerning);
if(kerning.x != 0)
dim.x += (unsigned)(kerning.x >> 6);
}
}
}
}
if(dim.x < (int)maxWidth)
dim.x = maxWidth;
return dim;
}
vec2i getDimension(int c, int lastC) const {
vec2i dim(0, glyphHeight);
FT_Activate_Size(size);
const Glyph* glyph = 0;
if(c <= 0xff) {
glyph = &lowChars[c];
}
else {
auto g = std::lower_bound(hiChars.begin(), hiChars.end(), c);
if(g != hiChars.end() && g->letter == c)
glyph = &g->glyph;
else
glyph = &lowChars['?'];
}
if(glyph) {
dim.x += (int)glyph->xAdv;
//Don't kern anything involving digits
if(c < '0' || c > '9') {
if(lastC) {
FT_Vector kerning;
FT_Get_Kerning(*face, lastC, glyph->glyph_index, 0, &kerning);
if(kerning.x != 0)
dim.x += (unsigned)(kerning.x >> 6);
}
}
}
return dim;
}
unsigned getBaseline() const {
return baseLine;
}
unsigned getLineHeight() const {
return glyphHeight;
}
FontFT2()
: lowChars(new Glyph[256]), glyphHeight(0), baseLine(0), textureHeight(1), textureWidth(1)
{
memset(lowChars, 0, sizeof(Glyph) * 256);
}
~FontFT2()
{
FT_Done_Size(size);
delete[] lowChars;
for(auto tex = textures.begin(), end = textures.end(); tex != end; ++tex)
delete *tex;
}
};
FT_Library* library = 0;
std::map<std::string, FT_Face*> faces;
const int PAGE_SIZE = 512;
Font* loadFontFT2(render::RenderDriver& driver, const char* filename,
std::vector<std::pair<int,int>>& pages, int size) {
FontFT2* font = new FontFT2();
font->textureWidth = PAGE_SIZE;
font->textureHeight = PAGE_SIZE;
std::vector<Image*> images;
int cur_page = 0;
Image* img = new Image(PAGE_SIZE, PAGE_SIZE, FMT_Alpha, 0xff);
images.push_back(img);
int x = 0, y = 0;
//Create the library if none exists
if(!library) {
library = new FT_Library();
FT_Init_FreeType(library);
}
//Read in the font face if necessary
FT_Face* face = 0;
auto it = faces.find(filename);
if(it != faces.end()) {
face = it->second;
}
else {
face = new FT_Face();
if (int err = FT_New_Face(*library, filename, 0, face)) {
fprintf(stderr, "Error loading font %s (code %d).\n", filename, err);
return 0;
}
faces[filename] = face;
}
FT_New_Size(*face, &font->size);
FT_Activate_Size(font->size);
FT_Set_Char_Size(*face, 0, size << 6, 96, 96);
font->x_scale = (double)((*face)->size->metrics.x_scale / 65536.0);
font->y_scale = (double)((*face)->size->metrics.y_scale / 65536.0);
font->glyphHeight = (unsigned)((*face)->size->metrics.height >> 6);
font->face = face;
font->baseLine = font->glyphHeight + (unsigned)((*face)->size->metrics.descender >> 6);
int lineHeight = 0;
for(auto page = pages.begin(), end = pages.end(); page != end; ++page) {
int from = page->first;
int to = page->second;
for(int ch = from; ch < to; ++ch) {
int glyph_ind = FT_Get_Char_Index(*face, ch);
FontFT2::Glyph glyph;
glyph.page = cur_page;
if(glyph_ind != 0) {
FT_Load_Glyph(*face, glyph_ind, FT_LOAD_RENDER);
auto glp = (*face)->glyph;
auto bmp = glp->bitmap;
//Collect data about glyph
glyph.draw = true;
glyph.glyph_index = glyph_ind;
glyph.w = glp->bitmap.width;
glyph.h = glp->bitmap.rows;
glyph.wOff = glp->bitmap_left;
glyph.hOff = font->baseLine - glp->bitmap_top;
glyph.xAdv = ((float)glp->metrics.horiAdvance) / 64.f;
glyph.x = x;
glyph.y = y;
if(glyph.h > lineHeight)
lineHeight = glyph.h;
//Find a fitting spot on the image
x += (unsigned)glyph.w + 2;
if(x > PAGE_SIZE) {
x = (unsigned)glyph.w + 2;
glyph.x = 0;
y += lineHeight + 1;
glyph.y += lineHeight + 1;
lineHeight = glyph.h;
if(y + font->glyphHeight + 1 > (unsigned)PAGE_SIZE) {
img = new Image(PAGE_SIZE, PAGE_SIZE, FMT_Alpha, 0xff);
images.push_back(img);
y = 0;
glyph.y = 0;
++cur_page;
++glyph.page;
}
}
//Put the glyph on the actual image
unsigned char* buffer = bmp.buffer;
for(int row = 0; row < bmp.rows; ++row, buffer += bmp.width)
memcpy(&img->get_alpha(glyph.x, glyph.y + row), buffer, bmp.width);
}
else {
glyph.draw = false;
glyph.xAdv = 0;
}
if(ch <= 0xff)
font->lowChars[ch] = glyph;
else
font->hiChars.push_back(FontFT2::GlyphEntry(ch, glyph));
}
}
//Create the textures
for(auto it = images.begin(), end = images.end(); it != end; ++it) {
auto material = new render::RenderState();
material->depthTest = render::DT_NoDepthTest;
material->lighting = false;
material->culling = render::FC_None;
material->baseMat = MAT_Font;
material->depthWrite = false;
if(load_resources)
material->textures[0] = resource::queueImage(*it, 20, true, false);
font->textures.push_back(material);
}
std::sort(font->hiChars.begin(), font->hiChars.end());
return font;
}
};
File diff suppressed because it is too large Load Diff
+6
View File
@@ -0,0 +1,6 @@
#pragma once
#include "render/driver.h"
namespace render {
RenderDriver* createGLDriver();
};
+137
View File
@@ -0,0 +1,137 @@
#include "gl_framebuffer.h"
#include "main/logging.h"
#include "main/references.h"
namespace render {
class DummyTexture : public Texture {
GLuint glName;
public:
DummyTexture(GLuint name) : glName(name) {
type = TT_2D;
hasMipMaps = false;
loaded = true;
}
virtual bool isPixelActive(vec2i px) const { return false; }
virtual void loadStart(Image& image, bool mipmap = true, bool cachePixels = false, unsigned lod = 0) {}
virtual void loadPartial(Image& image, const recti& pixels, bool cachePixels = false, unsigned lod = 0) {}
virtual void loadFinish(bool mipmap, unsigned lod = 0) {}
virtual void load(Image& image, bool mipmap = true, bool cachePixels = false) {}
virtual void save(Image& image) const {}
virtual void bind() { glBindTexture(GL_TEXTURE_2D, glName); }
virtual unsigned getID() const { return glName; }
virtual unsigned getTextureBytes() const { return 0; }
};
glFrameBuffer::glFrameBuffer(const vec2i& Size) {
devices.render->reportErrors();
type = TT_2D;
loaded = true;
size = Size;
hasMipMaps = false;
glGenFramebuffers(1, &buffer);
glBindFramebuffer(GL_FRAMEBUFFER, buffer);
prevRenderState.filterMin = TF_Nearest;
prevRenderState.filterMag = TF_Nearest;
textures = new GLuint[2];
glGenTextures(2, textures);
glBindTexture(GL_TEXTURE_2D, textures[0]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Size.width, Size.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glBindTexture(GL_TEXTURE_2D, 0);
glBindTexture(GL_TEXTURE_2D, textures[1]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, Size.width, Size.height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE);
glBindTexture(GL_TEXTURE_2D, 0);
devices.render->reportErrors("setting framebuffer parameters");
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textures[0], 0);
devices.render->reportErrors("creating framebuffer color attachment");
renderBuffers = new GLuint[1];
//glGenRenderbuffers(1, renderBuffers);
//glBindRenderbuffer(GL_RENDERBUFFER, renderBuffers[0]);
//glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, Size.width, Size.height);
//glBindRenderbuffer(GL_RENDERBUFFER, 0);
//glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderBuffers[0]);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, textures[1], 0);
depthTexture = new DummyTexture(textures[1]);
devices.render->reportErrors("creating framebuffer depth attachment");
auto state = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if(state != GL_FRAMEBUFFER_COMPLETE)
error("Incomplete framebuffer: %d", state);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
glFrameBuffer::~glFrameBuffer() {
glDeleteFramebuffers(1, &buffer);
glDeleteTextures(2, textures); delete[] textures;
//glDeleteRenderbuffers(1, renderBuffers);
delete[] renderBuffers;
delete depthTexture;
}
void glFrameBuffer::bind() {
glBindTexture(GL_TEXTURE_2D, textures[0]);
}
unsigned glFrameBuffer::getID() const {
return textures[0];
}
void glFrameBuffer::setAsTarget() {
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, buffer);
glViewport(0,0,size.width, size.height);
}
void glFrameBuffer::save(Image& image) const {
image.resize(size.width, size.height, FMT_RGB);
glBindTexture(GL_TEXTURE_2D, textures[0]);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_BYTE, image.rgb);
}
void glFrameBuffer::load(Image& img, bool mipmap, bool cachePixels) {
throw "Cannot load framebuffer from image.";
}
void glFrameBuffer::loadStart(Image& image, bool mipmap, bool cachePixels, unsigned lod) {
throw "Cannot load framebuffer from image.";
}
void glFrameBuffer::loadPartial(Image& image, const recti& pixels, bool cachePixels, unsigned lod) {
throw "Cannot load framebuffer from image.";
}
void glFrameBuffer::loadFinish(bool mipmap, unsigned lod) {
throw "Cannot load framebuffer from image.";
}
bool glFrameBuffer::isPixelActive(vec2i px) const {
throw "Cannot get framebuffer pixels.";
}
unsigned glFrameBuffer::getTextureBytes() const {
//4 bytes for RGBA, 3 bytes for 24 bit z buffer
return size.width * size.height * 7;
}
};
+28
View File
@@ -0,0 +1,28 @@
#include "texture.h"
#include "compat/gl.h"
namespace render {
class glFrameBuffer : public Texture {
GLuint buffer;
GLuint* textures;
GLuint* renderBuffers;
public:
glFrameBuffer(const vec2i& Size);
~glFrameBuffer();
Texture* depthTexture;
bool isPixelActive(vec2i px) const;
void bind();
unsigned getID() const;
void load(Image& img, bool mipmap = true, bool cachePixels = false);
void loadStart(Image& image, bool mipmap = true, bool cachePixels = false, unsigned lod = 0);
void loadPartial(Image& image, const recti& pixels, bool cachePixels = false, unsigned lod = 0);
void loadFinish(bool mipmap, unsigned lod = 0);
void save(Image& img) const;
unsigned getTextureBytes() const;
void setAsTarget();
};
};
+238
View File
@@ -0,0 +1,238 @@
#include "compat/gl.h"
#include "vertex.h"
#include "vec3.h"
#include "mesh.h"
#include "render/render_mesh.h"
#include "render/gl_mesh.h"
#include "render/driver.h"
#include "main/references.h"
#include <assert.h>
namespace render {
bool vertexArraysAvailable = false, vertArraysChecked = false;
bool useVertexArrays() {
if(vertArraysChecked)
return vertexArraysAvailable;
vertexArraysAvailable = GLEW_ARB_vertex_array_object;
vertArraysChecked = true;
return vertexArraysAvailable;
}
const RenderMesh* lastRenderedMesh = 0;
class GLMesh : public RenderMesh {
bool valid;
public:
GLuint vertex_buffer;
GLuint element_buffer;
GLuint tangent_buffer;
GLuint color_buffer;
GLuint uv2_buffer;
GLuint vertex_array;
unsigned int vertices;
unsigned int faces;
AABBoxf bbox;
Mesh mesh;
double lod_distance;
const RenderMesh* lod;
GLMesh() : valid(false), lod_distance(1), lod(0), tangent_buffer(0), vertex_array(0), color_buffer(0), uv2_buffer(0) {
}
GLMesh(const Mesh& mesh) : valid(false), lod_distance(1), lod(0), tangent_buffer(0), vertex_array(0), color_buffer(0), uv2_buffer(0) {
resetToMesh(mesh);
}
~GLMesh() {
clear();
}
const Mesh& getMesh() const {
return mesh;
}
void clear() {
if(!valid)
return;
glDeleteVertexArrays(1, &vertex_array);
glDeleteBuffers(1, &vertex_buffer);
glDeleteBuffers(1, &element_buffer);
if(tangent_buffer != 0) {
glDeleteBuffers(1, &tangent_buffer);
tangent_buffer = 0;
}
if(color_buffer != 0) {
glDeleteBuffers(1, &color_buffer);
color_buffer = 0;
}
if(uv2_buffer != 0) {
glDeleteBuffers(1, &uv2_buffer);
uv2_buffer = 0;
}
valid = false;
}
void setupBuffers() const {
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex,position));
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex,u));
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex,normal));
if(tangent_buffer) {
glBindBuffer(GL_ARRAY_BUFFER, tangent_buffer);
glEnableVertexAttribArray(3);
glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, sizeof(vec4f), nullptr);
}
else {
glDisableVertexAttribArray(3);
}
if(color_buffer) {
glBindBuffer(GL_ARRAY_BUFFER, color_buffer);
glEnableVertexAttribArray(4);
glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, sizeof(Colorf), nullptr);
}
else {
glDisableVertexAttribArray(4);
}
if(uv2_buffer) {
glBindBuffer(GL_ARRAY_BUFFER, uv2_buffer);
glEnableVertexAttribArray(5);
glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, sizeof(vec4f), nullptr);
}
else {
glDisableVertexAttribArray(5);
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_buffer);
}
void resetToMesh(const Mesh& mesh) {
this->mesh = mesh;
clear();
if(mesh.vertices.empty() || mesh.faces.empty())
return;
devices.render->reportErrors();
// Generate vertex buffer
vertices = (unsigned)mesh.vertices.size();
glGenBuffers(1, &vertex_buffer);
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
glBufferData(GL_ARRAY_BUFFER, vertices * sizeof(Vertex),
&mesh.vertices[0], GL_STATIC_DRAW);
if(!mesh.tangents.empty()) {
glGenBuffers(1, &tangent_buffer);
glBindBuffer(GL_ARRAY_BUFFER, tangent_buffer);
glBufferData(GL_ARRAY_BUFFER, mesh.tangents.size() * sizeof(vec4f), mesh.tangents.data(), GL_STATIC_DRAW);
}
if(!mesh.colors.empty()) {
glGenBuffers(1, &color_buffer);
glBindBuffer(GL_ARRAY_BUFFER, color_buffer);
glBufferData(GL_ARRAY_BUFFER, mesh.colors.size() * sizeof(Colorf), mesh.colors.data(), GL_STATIC_DRAW);
}
if(!mesh.uvs2.empty()) {
glGenBuffers(1, &uv2_buffer);
glBindBuffer(GL_ARRAY_BUFFER, uv2_buffer);
glBufferData(GL_ARRAY_BUFFER, mesh.uvs2.size() * sizeof(vec4f), mesh.uvs2.data(), GL_STATIC_DRAW);
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
// Generate element buffer
faces = (unsigned)mesh.faces.size();
glGenBuffers(1, &element_buffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_buffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, faces * sizeof(Mesh::Face),
&mesh.faces[0], GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
devices.render->reportErrors("Filling mesh buffers");
if(useVertexArrays()) {
glGenVertexArrays(1, &vertex_array);
glBindVertexArray(vertex_array);
setupBuffers();
glBindVertexArray(0);
devices.render->reportErrors("Setting up mesh vertex array");
}
//Generate Bounding Box
for(unsigned i = 0; i < vertices; ++i)
bbox.addPoint(mesh.vertices[i].position);
valid = true;
}
const RenderMesh* selectLOD(double distance) const {
if(lod && distance > lod_distance)
return lod->selectLOD(distance);
return this;
}
void setLOD(double distance, const RenderMesh* mesh) {
lod_distance = distance;
lod = mesh;
}
const AABBoxf& getBoundingBox() const {
return bbox;
}
unsigned getMeshBytes() const {
if(!valid)
return 0;
return (vertices * sizeof(float) * 8) + (faces * 3 * sizeof(short));
}
void render() const {
if(lastRenderedMesh != this) {
//We only need to check valid here, because we can't be the rendered mesh if we aren't valid
if(!valid)
return;
lastRenderedMesh = this;
if(vertex_array)
glBindVertexArray(vertex_array);
else
setupBuffers();
}
glDrawElements(
GL_TRIANGLES, //Mode
3 * faces, //Total amount of vertices
GL_UNSIGNED_SHORT, //Index type
(void*)0 //Offset
);
}
};
RenderMesh* createGLMesh(const Mesh& mesh) {
return new GLMesh(mesh);
}
};
+7
View File
@@ -0,0 +1,7 @@
#pragma once
#include "render/render_mesh.h"
#include "mesh.h"
namespace render {
RenderMesh* createGLMesh(const Mesh& mesh);
};
+925
View File
@@ -0,0 +1,925 @@
#include "render/shader.h"
#include "render/gl_shader.h"
#include "compat/gl.h"
#include "compat/misc.h"
#include "str_util.h"
#include "files.h"
#include "main/logging.h"
#include "main/references.h"
extern unsigned frameNumber;
namespace render {
void preProcessShader(std::string& shader) {
std::string output;
size_t start = 0;
while(start < shader.size()) {
size_t pos = shader.find('#', start);
if(pos == std::string::npos)
break;
if(pos != start)
output += shader.substr(start, pos-start);
if(shader.size() - pos > 3 && shader[pos+1] == '{' && shader[pos+2] == '{') {
size_t endpos = shader.find("}}", pos, 2);
if(endpos == std::string::npos)
break;
std::string value = shader.substr(pos+3, endpos-pos-3);
std::string replace;
if(value == "level:extreme") {
auto* sl = devices.settings.engine.getSetting("iShaderLevel");
if(sl)
output += (sl->getInteger() >= 4) ? "true" : "false";
}
else if(value == "level:high") {
auto* sl = devices.settings.engine.getSetting("iShaderLevel");
if(sl)
output += (sl->getInteger() >= 3) ? "true" : "false";
}
else if(value == "level:medium") {
auto* sl = devices.settings.engine.getSetting("iShaderLevel");
if(sl)
output += (sl->getInteger() >= 2) ? "true" : "false";
}
else if(value == "fallback") {
auto* fallback = devices.settings.engine.getSetting("bShaderFallback");
if(fallback)
output += (fallback->getBool()) ? "true" : "false";
}
else {
auto* setting = devices.settings.engine.getSetting(value.c_str());
if(!setting)
setting = devices.settings.mod.getSetting(value.c_str());
if(setting) {
switch(setting->type) {
case GT_Bool:
output += (setting->getBool()) ? "true" : "false";
break;
case GT_Integer:
case GT_Enum:
output += toString<int>(setting->getInteger());
break;
case GT_Double:
output += toString<double>(setting->getDouble());
break;
case GT_String:
output += *setting->getString();
break;
}
}
else {
error("ERROR: Could not find shader variable %s.", value.c_str());
}
}
start = endpos + 2;
}
else if(shader.size() - pos > 11 && shader.compare(pos, 10, "#include \"") == 0) {
size_t endpos = shader.find("\"", pos+10, 1);
if(endpos == std::string::npos)
break;
std::string value = shader.substr(pos+10, endpos-pos-10);
std::string fname = devices.mods.resolve(value);
if(!fileExists(fname)) {
error("ERROR COMPILING SHADER: Could not find include file %s. (Resolved to %s)", value.c_str(), fname.c_str());
}
else {
std::string includeContents = getFileContents(fname);
preProcessShader(includeContents);
output += includeContents;
}
start = endpos + 1;
}
else {
output += "#";
start = pos + 1;
}
}
if(start < shader.size())
output += shader.substr(start, shader.size()-start);
shader = output;
}
class GLShader;
class GLShaderProgram : public ShaderProgram {
public:
GLuint vertex_shader, fragment_shader, program;
std::string vertex_file, fragment_file;
std::unordered_map<GLuint, unsigned> cached_uniforms;
std::vector<float> mem_buffer;
const GLShader* lastShader;
GLShaderProgram(const std::string& vFile, const std::string& fFile)
: vertex_file(vFile), fragment_file(fFile), program(0), lastShader(0)
{
}
~GLShaderProgram() {
reset();
}
void reset() {
if(program != 0) {
glDeleteProgram(program);
program = 0;
}
mem_buffer.clear();
cached_uniforms.clear();
}
unsigned cacheUniform(GLuint position, size_t size) {
auto it = cached_uniforms.find(position);
if(it != cached_uniforms.end())
return it->second;
unsigned pos = (unsigned)mem_buffer.size();
cached_uniforms[position] = pos;
for(size_t i = 0; i < size; ++i)
mem_buffer.push_back(0);
return pos;
}
GLuint compileShader(GLenum type, const std::string& fname, const std::string& source) {
GLuint shader;
//Compile
const GLchar* str_ptr = (const GLchar*)source.c_str();
GLint str_len = (GLint)source.size();
shader = glCreateShader(type);
glShaderSource(shader, 1, &str_ptr, &str_len);
glCompileShader(shader);
//Check compile value
GLint shader_ok;
glGetShaderiv(shader, GL_COMPILE_STATUS, &shader_ok);
if(!shader_ok) {
error("Failed to compile shader: %s", fname.c_str());
GLint log_length;
char* log;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
log = (char*)malloc(log_length);
glGetShaderInfoLog(shader, log_length, NULL, log);
error("%s", log);
free(log);
glDeleteShader(shader);
return 0;
}
else if(getLogLevel() == LL_Info) {
//Print warnings in verbose
GLint log_length;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
//We're always provided at least an empty string, just ignore very small logs
if(log_length > 8) {
char* log;
log = (char*)malloc(log_length);
glGetShaderInfoLog(shader, log_length, NULL, log);
error("Shader '%s' has warnings:\n%s", fname.c_str(), log);
free(log);
}
}
return shader;
}
int compile() override {
reset();
//Compile vertex shader
{
auto contents = getFileContents(vertex_file);
preProcessShader(contents);
if(contents.empty()) {
error("Could not open vertex shader, or the file was empty");
return 1;
}
vertex_shader = compileShader(GL_VERTEX_SHADER, vertex_file, contents);
if(!vertex_shader)
return 1;
}
//Compile fragment shader
{
auto contents = getFileContents(fragment_file);
preProcessShader(contents);
fragment_shader = compileShader(GL_FRAGMENT_SHADER, fragment_file, contents);
if(!fragment_shader) {
if(contents.empty())
error("Could not open fragment shader, or the file was empty");
glDeleteShader(vertex_shader);
return 2;
}
}
//Link entire program
program = glCreateProgram();
glBindAttribLocation(program, 0, "in_vertex");
glBindAttribLocation(program, 2, "in_normal");
glBindAttribLocation(program, 1, "in_uv");
glBindAttribLocation(program, 3, "in_tangent");
glBindAttribLocation(program, 4, "in_color");
glBindAttribLocation(program, 5, "in_uv2");
glAttachShader(program, vertex_shader);
glAttachShader(program, fragment_shader);
glLinkProgram(program);
glDeleteShader(vertex_shader);
glDeleteShader(fragment_shader);
//Make sure it linked correctly
GLint status;
glGetProgramiv(program,GL_LINK_STATUS,&status);
if(status == GL_FALSE) {
error("Linking failed:");
GLint log_length;
char* log;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_length);
log = (char*)malloc(log_length);
glGetProgramInfoLog(program, log_length, NULL, log);
error("%s", log);
free(log);
glDeleteProgram(program);
program = 0;
return 3;
}
return 0;
}
};
class GLShader : public Shader {
public:
mutable unsigned lastBoundFrame;
std::vector<std::string> uniform_names;
std::vector<GLuint> uniform_locations;
std::vector<unsigned> uniform_vars;
std::vector<unsigned> uniform_cache;
GLShader()
{
program = 0;
constant = true;
dynamicFloats = 0;
lastBoundFrame = 0xffffffff;
}
~GLShader() {
reset();
}
void reset() {
uniform_locations.clear();
uniform_cache.clear();
uniform_vars.clear();
}
virtual int compile() {
reset();
if(!program) {
error("No associated program");
return 4;
}
auto glProgram = (GLShaderProgram*)program;
if(glProgram->program == 0)
return 4;
bool missingVars = false;
for(unsigned i = 0; i < uniform_names.size(); ++i) {
GLuint location = glGetUniformLocation(glProgram->program, uniform_names[i].c_str());
if(location == (GLuint)-1) {
//NOTE: This is no longer considered an error, because dynamic shader levels
//are going to be causing uniforms to be missing all the time.
//
//error("Unable to find uniform '%s'", uniform_names[i].c_str());
//missingVars = true;
}
else {
uniform_locations.push_back(location);
uniform_vars.push_back(i);
}
}
for(unsigned i = 0; i < uniform_locations.size(); ++i) {
auto& v = vars[uniform_vars[i]];
size_t size = 0;
switch(v.type) {
case VT_int: case VT_float: size = 1; break;
case VT_int2: case VT_float2: size = 2; break;
case VT_int3: case VT_float3: size = 3; break;
case VT_int4: case VT_float4: size = 4; break;
case VT_mat3: size = 9; break;
}
size *= v.count;
uniform_cache.push_back(glProgram->cacheUniform(uniform_locations[i], size));
}
if(missingVars)
return 4;
else
return 0;
}
virtual void addVariable(const std::string& name, const Variable& var) {
uniform_names.push_back(name);
Shader::addVariable(name, var);
}
virtual void bind(float* dynamic) const {
if(!program) {
glUseProgram(0);
return;
}
auto glProgram = (GLShaderProgram*)program;
if(auto programID = glProgram->program) {
glUseProgram(programID);
}
else {
glUseProgram(0);
return;
}
const bool bindConst = lastBoundFrame != frameNumber || glProgram->lastShader != this;
lastBoundFrame = frameNumber;
glProgram->lastShader = this;
if(dynamic) {
float* floats = dynamic;
for(size_t i = 0, cnt = uniform_locations.size(); i < cnt; ++i) {
const Shader::Variable& var = vars[uniform_vars[i]];
if(!bindConst && var.constant)
continue;
GLuint uniform = uniform_locations[i];
void* uniformMem = (void*)&glProgram->mem_buffer[uniform_cache[i]];
switch(var.type) {
case VT_invalid:
break;
case VT_int:
if(var.constant) {
if(var._intcall)
var._intcall(var._ints, var.count, var._args);
if(memcmp(uniformMem, var._ints, var.count * 4) != 0) {
glUniform1iv(uniform, var.count, var._ints);
memcpy(uniformMem, var._ints, var.count * 4);
}
}
else if(var._intcall) {
if(memcmp(uniformMem, floats, var.count * 4) != 0) {
glUniform1iv(uniform, var.count, (int*)floats);
memcpy(uniformMem, floats, var.count * 4);
}
floats += var.count;
}
break;
case VT_int2:
if(var.constant) {
if(var._intcall)
var._intcall(var._ints, var.count, var._args);
if(memcmp(uniformMem, var._ints, var.count * 8) != 0) {
glUniform2iv(uniform, var.count, var._ints);
memcpy(uniformMem, var._ints, var.count * 8);
}
}
else if(var._intcall) {
if(memcmp(uniformMem, floats, var.count * 8) != 0) {
glUniform2iv(uniform, var.count, (int*)floats);
memcpy(uniformMem, floats, var.count * 8);
}
floats += var.count * 2;
}
break;
case VT_int3:
if(var.constant) {
if(var._intcall)
var._intcall(var._ints, var.count, var._args);
if(memcmp(uniformMem, var._ints, var.count * 12) != 0) {
glUniform3iv(uniform, var.count, var._ints);
memcpy(uniformMem, var._ints, var.count * 12);
}
}
else if(var._intcall) {
if(memcmp(uniformMem, floats, var.count * 12) != 0) {
glUniform3iv(uniform, var.count, (int*)floats);
memcpy(uniformMem, floats, var.count * 12);
}
floats += var.count * 3;
}
break;
case VT_int4:
if(var.constant) {
if(var._intcall)
var._intcall(var._ints, var.count, var._args);
if(memcmp(uniformMem, var._ints, var.count * 16) != 0) {
glUniform4iv(uniform, var.count, var._ints);
memcpy(uniformMem, var._ints, var.count * 16);
}
}
else if(var._intcall) {
if(memcmp(uniformMem, floats, var.count * 16) != 0) {
glUniform4iv(uniform, var.count, (int*)floats);
memcpy(uniformMem, floats, var.count * 16);
}
floats += var.count * 4;
}
break;
case VT_float:
if(var.constant) {
if(var._floatcall)
var._floatcall(var._floats, var.count, var._args);
if(memcmp(uniformMem, var._floats, var.count * 4) != 0) {
glUniform1fv(uniform, var.count, var._floats);
memcpy(uniformMem, var._floats, var.count * 4);
}
}
else if(var._intcall) {
if(memcmp(uniformMem, floats, var.count * 4) != 0) {
glUniform1fv(uniform, var.count, floats);
memcpy(uniformMem, floats, var.count * 4);
}
floats += var.count;
}
break;
case VT_float2:
if(var.constant) {
if(var._floatcall)
var._floatcall(var._floats, var.count, var._args);
if(memcmp(uniformMem, var._floats, var.count * 8) != 0) {
glUniform2fv(uniform, var.count, var._floats);
memcpy(uniformMem, var._floats, var.count * 8);
}
}
else if(var._intcall) {
if(memcmp(uniformMem, floats, var.count * 8) != 0) {
glUniform2fv(uniform, var.count, floats);
memcpy(uniformMem, floats, var.count * 8);
}
floats += var.count * 2;
}
break;
case VT_float3:
if(var.constant) {
if(var._floatcall)
var._floatcall(var._floats, var.count, var._args);
if(memcmp(uniformMem, var._floats, var.count * 12) != 0) {
glUniform3fv(uniform, var.count, var._floats);
memcpy(uniformMem, var._floats, var.count * 12);
}
}
else if(var._intcall) {
if(memcmp(uniformMem, floats, var.count * 12) != 0) {
glUniform3fv(uniform, var.count, floats);
memcpy(uniformMem, floats, var.count * 12);
}
floats += var.count * 3;
}
break;
case VT_float4:
if(var.constant) {
if(var._floatcall)
var._floatcall(var._floats, var.count, var._args);
if(memcmp(uniformMem, var._floats, var.count * 16) != 0) {
glUniform4fv(uniform, var.count, var._floats);
memcpy(uniformMem, var._floats, var.count * 16);
}
}
else if(var._intcall) {
if(memcmp(uniformMem, floats, var.count * 16) != 0) {
glUniform4fv(uniform, var.count, floats);
memcpy(uniformMem, floats, var.count * 16);
}
floats += var.count * 4;
}
break;
case VT_mat3:
if(var.constant) {
if(var._floatcall)
var._floatcall(var._floats, var.count, var._args);
if(memcmp(uniformMem, var._floats, var.count * 36) != 0) {
glUniformMatrix3fv(uniform, var.count, false, var._floats);
memcpy(uniformMem, var._floats, var.count * 36);
}
}
else if(var._intcall) {
if(memcmp(uniformMem, floats, var.count * 36) != 0) {
glUniformMatrix3fv(uniform, var.count, false, floats);
memcpy(uniformMem, floats, var.count * 36);
}
floats += var.count * 9;
}
NO_DEFAULT
}
}
}
else {
for(size_t i = 0, cnt = uniform_locations.size(); i < cnt; ++i) {
const Shader::Variable& var = vars[uniform_vars[i]];
if(!bindConst && var.constant)
continue;
GLuint uniform = uniform_locations[i];
void* uniformMem = (void*)&glProgram->mem_buffer[uniform_cache[i]];
switch(var.type) {
case VT_invalid:
break;
case VT_int:
if(var._intcall)
var._intcall(var._ints,var.count,var._args);
if(memcmp(uniformMem, var._ints, var.count * 4) != 0) {
glUniform1iv(uniform, var.count, var._ints);
memcpy(uniformMem, var._ints, var.count * 4);
}
break;
case VT_int2:
if(var._intcall)
var._intcall(var._ints,var.count,var._args);
if(memcmp(uniformMem, var._ints, var.count * 8) != 0) {
glUniform2iv(uniform, var.count, var._ints);
memcpy(uniformMem, var._ints, var.count * 8);
}
break;
case VT_int3:
if(var._intcall)
var._intcall(var._ints,var.count,var._args);
if(memcmp(uniformMem, var._ints, var.count * 12) != 0) {
glUniform3iv(uniform, var.count, var._ints);
memcpy(uniformMem, var._ints, var.count * 12);
}
break;
case VT_int4:
if(var._intcall)
var._intcall(var._ints,var.count,var._args);
if(memcmp(uniformMem, var._ints, var.count * 16) != 0) {
glUniform4iv(uniform, var.count, var._ints);
memcpy(uniformMem, var._ints, var.count * 16);
}
break;
case VT_float:
if(var._floatcall)
var._floatcall(var._floats,var.count,var._args);
if(memcmp(uniformMem, var._floats, var.count * 4) != 0) {
glUniform1fv(uniform, var.count, var._floats);
memcpy(uniformMem, var._floats, var.count * 4);
}
break;
case VT_float2:
if(var._floatcall)
var._floatcall(var._floats,var.count,var._args);
if(memcmp(uniformMem, var._floats, var.count * 8) != 0) {
glUniform2fv(uniform, var.count, var._floats);
memcpy(uniformMem, var._floats, var.count * 8);
}
break;
case VT_float3:
if(var._floatcall)
var._floatcall(var._floats,var.count,var._args);
if(memcmp(uniformMem, var._floats, var.count * 12) != 0) {
glUniform3fv(uniform, var.count, var._floats);
memcpy(uniformMem, var._floats, var.count * 12);
}
break;
case VT_float4:
if(var._floatcall)
var._floatcall(var._floats,var.count,var._args);
if(memcmp(uniformMem, var._floats, var.count * 16) != 0) {
glUniform4fv(uniform, var.count, var._floats);
memcpy(uniformMem, var._floats, var.count * 16);
}
break;
case VT_mat3:
if(var._floatcall)
var._floatcall(var._floats,var.count,var._args);
if(memcmp(uniformMem, var._floats, var.count * 36) != 0) {
glUniformMatrix3fv(uniform, var.count, false, var._floats);
memcpy(uniformMem, var._floats, var.count * 36);
}
break;
NO_DEFAULT
}
}
}
}
void updateDynamicVars() const {
for(size_t i = 0, cnt = uniform_locations.size(); i < cnt; ++i) {
const Shader::Variable& var = vars[uniform_vars[i]];
if(var.constant)
continue;
GLuint uniform = uniform_locations[i];
void* uniformMem = (void*)&((GLShaderProgram*)program)->mem_buffer[uniform_cache[i]];
switch(var.type) {
case VT_invalid:
break;
case VT_int:
if(var._intcall)
var._intcall(var._ints,var.count,var._args);
if(memcmp(uniformMem, var._ints, var.count * 4) != 0) {
glUniform1iv(uniform, var.count, var._ints);
memcpy(uniformMem, var._ints, var.count * 4);
}
break;
case VT_int2:
if(var._intcall)
var._intcall(var._ints,var.count,var._args);
if(memcmp(uniformMem, var._ints, var.count * 8) != 0) {
glUniform2iv(uniform, var.count, var._ints);
memcpy(uniformMem, var._ints, var.count * 8);
}
break;
case VT_int3:
if(var._intcall)
var._intcall(var._ints,var.count,var._args);
if(memcmp(uniformMem, var._ints, var.count * 12) != 0) {
glUniform3iv(uniform, var.count, var._ints);
memcpy(uniformMem, var._ints, var.count * 12);
}
break;
case VT_int4:
if(var._intcall)
var._intcall(var._ints,var.count,var._args);
if(memcmp(uniformMem, var._ints, var.count * 16) != 0) {
glUniform4iv(uniform, var.count, var._ints);
memcpy(uniformMem, var._ints, var.count * 16);
}
break;
case VT_float:
if(var._floatcall)
var._floatcall(var._floats,var.count,var._args);
if(memcmp(uniformMem, var._floats, var.count * 4) != 0) {
glUniform1fv(uniform, var.count, var._floats);
memcpy(uniformMem, var._floats, var.count * 4);
}
break;
case VT_float2:
if(var._floatcall)
var._floatcall(var._floats,var.count,var._args);
if(memcmp(uniformMem, var._floats, var.count * 8) != 0) {
glUniform2fv(uniform, var.count, var._floats);
memcpy(uniformMem, var._floats, var.count * 8);
}
break;
case VT_float3:
if(var._floatcall)
var._floatcall(var._floats,var.count,var._args);
if(memcmp(uniformMem, var._floats, var.count * 12) != 0) {
glUniform3fv(uniform, var.count, var._floats);
memcpy(uniformMem, var._floats, var.count * 12);
}
break;
case VT_float4:
if(var._floatcall)
var._floatcall(var._floats,var.count,var._args);
if(memcmp(uniformMem, var._floats, var.count * 16) != 0) {
glUniform4fv(uniform, var.count, var._floats);
memcpy(uniformMem, var._floats, var.count * 16);
}
break;
case VT_mat3:
if(var._floatcall)
var._floatcall(var._floats,var.count,var._args);
if(memcmp(uniformMem, var._floats, var.count * 36) != 0) {
glUniformMatrix3fv(uniform, var.count, false, var._floats);
memcpy(uniformMem, var._floats, var.count * 36);
}
break;
NO_DEFAULT
}
}
}
void saveDynamicVars(float* buffer) const {
float* floats = buffer;
for(size_t i = 0, cnt = uniform_locations.size(); i < cnt; ++i) {
const Shader::Variable& var = vars[uniform_vars[i]];
if(var.constant)
continue;
switch(var.type) {
case VT_invalid:
break;
case VT_int:
if(var._intcall) {
var._intcall((int*)floats,var.count,var._args);
floats += var.count;
}
break;
case VT_int2:
if(var._intcall) {
var._intcall((int*)floats,var.count,var._args);
floats += var.count * 2;
}
break;
case VT_int3:
if(var._intcall) {
var._intcall((int*)floats,var.count,var._args);
floats += var.count * 3;
}
break;
case VT_int4:
if(var._intcall) {
var._intcall((int*)floats,var.count,var._args);
floats += var.count * 4;
}
break;
case VT_float:
if(var._floatcall) {
var._floatcall(floats,var.count,var._args);
floats += var.count;
}
break;
case VT_float2:
if(var._floatcall) {
var._floatcall(floats,var.count,var._args);
floats += var.count * 2;
}
break;
case VT_float3:
if(var._floatcall) {
var._floatcall(floats,var.count,var._args);
floats += var.count * 3;
}
break;
case VT_float4:
if(var._floatcall) {
var._floatcall(floats,var.count,var._args);
floats += var.count * 4;
}
break;
case VT_mat3:
if(var._floatcall) {
var._floatcall(floats,var.constant,var._args);
floats += var.constant * 9;
}
NO_DEFAULT
}
}
}
void loadDynamicVars(float* buffer) const {
float* floats = buffer;
for(size_t i = 0, cnt = uniform_locations.size(); i < cnt; ++i) {
const Shader::Variable& var = vars[uniform_vars[i]];
if(var.constant)
continue;
GLuint uniform = uniform_locations[i];
void* uniformMem = (void*)&((GLShaderProgram*)program)->mem_buffer[uniform_cache[i]];
switch(var.type) {
case VT_invalid:
break;
case VT_int:
if(var._intcall) {
if(memcmp(uniformMem, floats, var.count * 4) != 0) {
glUniform1iv(uniform, var.count, (int*)floats);
memcpy(uniformMem, floats, var.count * 4);
}
floats += var.count;
}
break;
case VT_int2:
if(var._intcall) {
if(memcmp(uniformMem, floats, var.count * 8) != 0) {
glUniform2iv(uniform, var.count, (int*)floats);
memcpy(uniformMem, floats, var.count * 8);
}
floats += var.count * 2;
}
break;
case VT_int3:
if(var._intcall) {
if(memcmp(uniformMem, floats, var.count * 12) != 0) {
glUniform3iv(uniform, var.count, (int*)floats);
memcpy(uniformMem, floats, var.count * 12);
}
floats += var.count * 3;
}
break;
case VT_int4:
if(var._intcall) {
if(memcmp(uniformMem, floats, var.count * 16) != 0) {
glUniform4iv(uniform, var.count, (int*)floats);
memcpy(uniformMem, floats, var.count * 16);
}
floats += var.count * 4;
}
break;
case VT_float:
if(var._floatcall) {
if(memcmp(uniformMem, floats, var.count * 4) != 0) {
glUniform1fv(uniform, var.count, floats);
memcpy(uniformMem, floats, var.count * 4);
}
floats += var.count;
}
break;
case VT_float2:
if(var._floatcall) {
if(memcmp(uniformMem, floats, var.count * 8) != 0) {
glUniform2fv(uniform, var.count, floats);
memcpy(uniformMem, floats, var.count * 8);
}
floats += var.count * 2;
}
break;
case VT_float3:
if(var._floatcall) {
if(memcmp(uniformMem, floats, var.count * 12) != 0) {
glUniform3fv(uniform, var.count, floats);
memcpy(uniformMem, floats, var.count * 12);
}
floats += var.count * 3;
}
break;
case VT_float4:
if(var._floatcall) {
if(memcmp(uniformMem, floats, var.count * 16) != 0) {
glUniform4fv(uniform, var.count, floats);
memcpy(uniformMem, floats, var.count * 16);
}
floats += var.count * 4;
}
break;
case VT_mat3:
if(var._floatcall) {
if(memcmp(uniformMem, floats, var.count * 36) != 0) {
glUniformMatrix3fv(uniform, var.count, false, floats);
memcpy(uniformMem, floats, var.count * 36);
}
floats += var.count * 9;
}
break;
NO_DEFAULT
}
}
}
};
Shader* createGLShader() {
return new GLShader();
}
ShaderProgram* createGLShaderProgram(const char* vertex_shader, const char* fragment_shader) {
return new GLShaderProgram(vertex_shader, fragment_shader);
}
};
+7
View File
@@ -0,0 +1,7 @@
#pragma once
#include "render/shader.h"
namespace render {
Shader* createGLShader();
ShaderProgram* createGLShaderProgram(const char* vertex_shader, const char* fragment_shader);
};
+423
View File
@@ -0,0 +1,423 @@
#include "render/gl_texture.h"
#include "main/references.h"
#include "main/logging.h"
#include <assert.h>
#include <algorithm>
namespace render {
extern bool isIntelCard;
void GLTexture::bind() {
glBindTexture(GL_TEXTURE_2D, glName);
}
unsigned GLTexture::getID() const {
return glName;
}
GLTexture::GLTexture()
: glName(0), pixelDepth(1)
{
type = TT_2D;
loaded = false;
}
GLTexture::GLTexture(Image& image, bool mipmap, bool cachePixels)
: glName(0), pixelDepth(1)
{
type = TT_2D;
loaded = false;
load(image, mipmap, cachePixels);
}
void createTexture(Image& image, bool mipmap) {
GLenum format;
unsigned levels = 1;
if(mipmap) {
unsigned smallDim = std::min(image.width, image.height);
while(smallDim > 2) {
smallDim /= 2;
levels += 1;
}
}
if(GLEW_ARB_texture_storage) {
switch(image.format) {
case FMT_Grey:
format = GL_LUMINANCE8;
//{
// GLint swizzleMask[] = {GL_RED, GL_RED, GL_RED, GL_ONE};
// glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask);
//}
break;
case FMT_Alpha:
format = GL_ALPHA8;
//{
// GLint swizzleMask[] = {GL_ONE, GL_ONE, GL_ONE, GL_RED};
// glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask);
//}
break;
case FMT_RGB:
format = GL_RGB8; break;
case FMT_RGBA:
format = GL_RGBA8; break;
NO_DEFAULT
}
glTexStorage2D(GL_TEXTURE_2D, levels, format, image.width, image.height);
if(!isIntelCard)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, levels-1);
}
else {
//Sized lum/alpha are from texture storage arb
switch(image.format) {
case FMT_Grey:
format = GL_LUMINANCE; break;
case FMT_Alpha:
format = GL_ALPHA; break;
case FMT_RGB:
format = GL_RGB; break;
case FMT_RGBA:
format = GL_RGBA; break;
NO_DEFAULT
}
unsigned w = image.width, h = image.height;
for(unsigned i = 0; i < levels; ++i) {
glTexImage2D(GL_TEXTURE_2D, i, format, w, h, 0, format, GL_UNSIGNED_BYTE, nullptr);
w /= 2;
h /= 2;
}
if(!isIntelCard)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, levels-1);
}
devices.render->reportErrors("creating texture");
}
void loadTexture(Image& image, const recti& pixels, unsigned lod = 0) {
devices.render->reportErrors();
GLenum format;
switch(image.format) {
case FMT_Grey:
format = GL_LUMINANCE; break;
case FMT_Alpha:
format = GL_ALPHA; break;
case FMT_RGB:
format = GL_RGB; break;
case FMT_RGBA:
format = GL_RGBA; break;
NO_DEFAULT
}
assert((unsigned)(pixels.topLeft.x + pixels.getWidth()) <= image.width);
assert((unsigned)(pixels.topLeft.y + pixels.getHeight()) <= image.height);
if(pixels.getSize() == vec2i(image.width, image.height)) {
void* ptrPixels;
switch(image.format) {
case FMT_Grey:
ptrPixels = (void*)image.grey; break;
case FMT_Alpha:
ptrPixels = (void*)image.grey; break;
case FMT_RGB:
ptrPixels = (void*)image.rgb; break;
case FMT_RGBA:
ptrPixels = (void*)image.rgba; break;
NO_DEFAULT
}
glTexSubImage2D(GL_TEXTURE_2D, lod, pixels.topLeft.x, pixels.topLeft.y, pixels.getWidth(), pixels.getHeight(), format, GL_UNSIGNED_BYTE, ptrPixels);
}
else if((unsigned)pixels.getWidth() == image.width) {
assert(pixels.topLeft.x == 0);
void* ptrPixels;
switch(image.format) {
case FMT_Grey:
ptrPixels = (void*)&image.get_grey(0, pixels.topLeft.y); break;
case FMT_Alpha:
ptrPixels = (void*)&image.get_alpha(0, pixels.topLeft.y); break;
case FMT_RGB:
ptrPixels = (void*)&image.get_rgb(0, pixels.topLeft.y); break;
case FMT_RGBA:
ptrPixels = (void*)&image.get_rgba(0, pixels.topLeft.y); break;
NO_DEFAULT
}
glTexSubImage2D(GL_TEXTURE_2D, lod, pixels.topLeft.x, pixels.topLeft.y, pixels.getWidth(), pixels.getHeight(), format, GL_UNSIGNED_BYTE, ptrPixels);
}
else {
//Must load line by line
for(unsigned y = pixels.topLeft.y, endY = pixels.botRight.y; y < endY; ++y) {
void* ptrPixels;
switch(image.format) {
case FMT_Grey:
ptrPixels = (void*)&image.get_grey(pixels.topLeft.x, y); break;
case FMT_Alpha:
ptrPixels = (void*)&image.get_alpha(pixels.topLeft.x, y); break;
case FMT_RGB:
ptrPixels = (void*)&image.get_rgb(pixels.topLeft.x, y); break;
case FMT_RGBA:
ptrPixels = (void*)&image.get_rgba(pixels.topLeft.x, y); break;
NO_DEFAULT
}
glTexSubImage2D(GL_TEXTURE_2D, lod, pixels.topLeft.x, y, pixels.getWidth(), 1, format, GL_UNSIGNED_BYTE, ptrPixels);
}
}
devices.render->reportErrors("filling sub image");
}
void GLTexture::loadStart(Image& image, bool mipmap, bool cachePixels, unsigned lod) {
devices.render->reportErrors();
loaded = true;
if(lod == 0) {
//Destroy old texture
if(glName != 0) {
glDeleteTextures(1, &glName);
glName = 0;
}
//Generate a new texture
glGenTextures(1,&glName);
//Store texture size
size = vec2i(image.width, image.height);
pixelDepth = ColorDepths[image.format];
if(cachePixels)
activePixels.resize(size.x * size.y);
}
glBindTexture(GL_TEXTURE_2D, glName);
if(lod == 0)
createTexture(image, mipmap);
}
void GLTexture::loadPartial(Image& image, const recti& pixels, bool cachePixels, unsigned lod) {
glBindTexture(GL_TEXTURE_2D, glName);
loadTexture(image, pixels, lod);
if(cachePixels && lod == 0) {
for(int y = pixels.topLeft.y; y < pixels.getHeight(); ++y) {
for(int x = pixels.topLeft.x; x < pixels.getWidth(); ++x) {
unsigned char a = image.get(x, y).a;
activePixels[y * size.width + x] = a != 0;
}
}
}
}
void GLTexture::loadFinish(bool mipmap, unsigned lod) {
devices.render->reportErrors();
glBindTexture(GL_TEXTURE_2D, glName);
hasMipMaps = mipmap;
if(mipmap) {
//Set linear mipmap filter
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
if(lod == 0) {
if(size.width * size.height > (1024 * 1024))
glHint(GL_GENERATE_MIPMAP_HINT, GL_FASTEST);
else
glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST);
glGenerateMipmap(GL_TEXTURE_2D);
}
}
else if(lod == 0) {
//Set linear filter
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
else {
//We're defining mipmap data now, switch back to mipmaping
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, lod);
}
devices.render->reportErrors("finalizing texture");
}
unsigned GLTexture::getTextureBytes() const {
unsigned bytes = size.width * size.height * pixelDepth;
if(hasMipMaps)
bytes += bytes / 3;
return bytes;
}
void GLTexture::load(Image& image, bool mipmap, bool cachePixels) {
loadStart(image, mipmap, cachePixels);
loadPartial(image, recti(0,0, image.width, image.height), cachePixels);
loadFinish(mipmap);
}
void GLTexture::save(Image& image) const {
image.resize(size.width, size.height, FMT_RGBA);
glBindTexture(GL_TEXTURE_2D, glName);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, image.rgba);
}
bool GLTexture::isPixelActive(vec2i px) const {
unsigned index = px.y * size.width + px.x;
if(activePixels.empty())
return true;
if(index >= activePixels.size())
return false;
return activePixels[index];
}
GLTexture::~GLTexture() {
glDeleteTextures(1, &glName);
}
bool GLCubeMap::isPixelActive(vec2i px) const {
return true;
}
void GLCubeMap::load(Image& image, bool mipmap, bool cachePixels) {
devices.render->reportErrors();
loaded = false;
vec2u tile = vec2u(image.width / 4, image.height / 3);
if(tile.width != tile.height || tile.width == 0) {
error("ERROR loading cubemap: Cubemap must have a 4:3 aspect ratio.");
return;
}
//Destroy old texture
if(glName != 0) {
glDeleteTextures(1, &glName);
glName = 0;
}
//Generate a new texture
glGenTextures(1,&glName);
//Store texture size
size = vec2i(image.width, image.height);
loaded = true;
pixelDepth = ColorDepths[image.format];
glBindTexture(GL_TEXTURE_CUBE_MAP, glName);
unsigned char* data = image.grey;
GLenum format;
switch(image.format) {
case FMT_Grey:
format = GL_LUMINANCE; break;
case FMT_Alpha:
format = GL_ALPHA; break;
case FMT_RGB:
format = GL_RGB; break;
case FMT_RGBA:
format = GL_RGBA; break;
NO_DEFAULT
}
auto getTile = [&](unsigned x, unsigned y) -> Image* {
vec2u top = vec2u(tile.width * x, tile.height * y);
return image.crop(rect<unsigned>(top, top + tile));
};
auto* img = getTile(1,1);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, format, tile.width, tile.height, 0, format, GL_UNSIGNED_BYTE, img->grey);
delete img;
img = getTile(2,1);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, format, tile.width, tile.height, 0, format, GL_UNSIGNED_BYTE, img->grey);
delete img;
img = getTile(1,2);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, format, tile.width, tile.height, 0, format, GL_UNSIGNED_BYTE, img->grey);
delete img;
img = getTile(3,1);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, format, tile.width, tile.height, 0, format, GL_UNSIGNED_BYTE, img->grey);
delete img;
img = getTile(0,1);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, format, tile.width, tile.height, 0, format, GL_UNSIGNED_BYTE, img->grey);
delete img;
img = getTile(1,0);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, format, tile.width, tile.height, 0, format, GL_UNSIGNED_BYTE, img->grey);
delete img;
devices.render->reportErrors("loading cubemap images");
hasMipMaps = mipmap;
if(mipmap) {
//Set linear mipmap filter
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
if(size.width * size.height > (1024 * 1024))
glHint(GL_GENERATE_MIPMAP_HINT, GL_FASTEST);
else
glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST);
glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
}
else {
//Set linear filter
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
devices.render->reportErrors("finalizing texture");
}
unsigned GLCubeMap::getTextureBytes() const {
unsigned bytes = size.width * size.height * pixelDepth * 6;
if(hasMipMaps)
bytes += bytes / 3;
return bytes;
}
void GLCubeMap::save(Image& image) const {}
void GLCubeMap::bind() {
glBindTexture(GL_TEXTURE_CUBE_MAP, glName);
}
unsigned GLCubeMap::getID() const {
return glName;
}
GLCubeMap::GLCubeMap(Image& image, bool mipmap) : glName(0), pixelDepth(1) {
loaded = false;
type = TT_Cubemap;
load(image, mipmap);
}
GLCubeMap::GLCubeMap() : glName(0), pixelDepth(0) {
loaded = false;
type = TT_Cubemap;
}
GLCubeMap::~GLCubeMap() {
glDeleteTextures(1, &glName);
}
void GLCubeMap::loadStart(Image& image, bool mipmap, bool cachePixels, unsigned lod) {
load(image, mipmap);
}
void GLCubeMap::loadPartial(Image& image, const recti& pixels, bool cachePixels, unsigned lod) {
}
void GLCubeMap::loadFinish(bool mipmap, unsigned lod) {
}
};
+53
View File
@@ -0,0 +1,53 @@
#pragma once
#include "compat/gl.h"
#include "render/texture.h"
#include "image.h"
#include "rect.h"
#include <vector>
namespace render {
class GLTexture : public Texture {
GLuint glName;
unsigned pixelDepth;
std::vector<bool> activePixels;
public:
bool isPixelActive(vec2i px) const;
void load(Image& image, bool mipmap = true, bool cachePixels = false);
void loadStart(Image& image, bool mipmap = true, bool cachePixels = false, unsigned lod = 0);
void loadPartial(Image& image, const recti& pixels, bool cachePixels = false, unsigned lod = 0);
void loadFinish(bool mipmap, unsigned lod = 0);
unsigned getTextureBytes() const;
void save(Image& image) const;
void bind();
unsigned getID() const;
GLTexture(Image& image, bool mipmap = true, bool cachePixels = false);
GLTexture();
~GLTexture();
};
class GLCubeMap : public Texture {
GLuint glName;
unsigned pixelDepth;
public:
bool isPixelActive(vec2i px) const;
void load(Image& image, bool mipmap = true, bool cachePixels = false);
void loadStart(Image& image, bool mipmap = true, bool cachePixels = false, unsigned lod = 0);
void loadPartial(Image& image, const recti& pixels, bool cachePixels = false, unsigned lod = 0);
void loadFinish(bool mipmap, unsigned lod = 0);
unsigned getTextureBytes() const;
void save(Image& image) const;
void bind();
unsigned getID() const;
GLCubeMap(Image& image, bool mipmap = true);
GLCubeMap();
~GLCubeMap();
};
};
+316
View File
@@ -0,0 +1,316 @@
#include "vertexBuffer.h"
#include "compat/gl.h"
#include "main/references.h"
#include "compat/misc.h"
#include <unordered_map>
const unsigned maxVerts = 2048, maxInds = 4096;
unsigned drawnSteps = 0, bufferFlushes = 0;
namespace render {
unsigned vbFloatLimit = 24, vbMaxSteps = 200;
unsigned vbFlushCounts[FC_COUNT] = {0,0,0,0};
extern const RenderMesh* lastRenderedMesh;
extern float* shaderUniforms;
VertexBufferTCV tcv;
threads::Mutex bufferLock;
extern bool useVertexArrays();
inline bool getRequestSize(unsigned& indexCount, unsigned& vertCount, unsigned polygons, unsigned polyType) {
switch(polyType) {
case PT_Lines:
indexCount = polygons * 2;
vertCount = polygons * 2;
return true;
case PT_LineStrip:
indexCount = polygons * 2;
vertCount = polygons + 1;
return true;
case PT_Triangles:
indexCount = polygons * 3;
vertCount = polygons * 3;
return false;
case PT_Quads:
indexCount = polygons * 6;
vertCount = polygons * 4;
return false;
NO_DEFAULT
}
}
void buildIndices(std::vector<unsigned short>& indices, unsigned indexCount, unsigned prevVerts, unsigned polygons, unsigned polyType) {
//Fill out indices according to the polygon type
auto prevInds = indices.size();
indices.resize(prevInds + indexCount);
unsigned short* pIndices = &indices.at(0);
switch(polyType) {
case PT_Lines:
for(unsigned i = 0; i < polygons * 2; ++i)
pIndices[prevInds + i] = prevVerts + i;
break;
case PT_LineStrip:
for(unsigned i = 0; i < polygons; ++i) {
pIndices[prevInds + (i*2)] = prevVerts + i;
pIndices[prevInds + (i*2) + 1] = prevVerts + i + 1;
}
break;
case PT_Triangles:
for(unsigned i = 0; i < polygons * 3; ++i)
pIndices[prevInds + i] = prevVerts + i;
break;
case PT_Quads:
for(unsigned i = 0; i < polygons; ++i) {
auto off = prevInds + (i*6);
auto vOff = prevVerts + (i*4);
pIndices[off] = vOff;
pIndices[off+1] = vOff+2;
pIndices[off+2] = vOff+1;
pIndices[off+3] = vOff;
pIndices[off+4] = vOff+3;
pIndices[off+5] = vOff+2;
}
break;
NO_DEFAULT
}
}
void renderVertexBuffers() {
tcv.draw();
}
VertexBufferTCV::VertexBufferTCV()
: last(0)
{
vertices.reserve(maxVerts);
indices.reserve(maxInds);
steps.reserve(vbMaxSteps);
shaderBufferSize = 40000;
shaderBuffer = new float[shaderBufferSize];
pNextShaderBuffer = shaderBuffer;
}
VertexBufferTCV::~VertexBufferTCV() {
for(auto i = vertBuffer.begin(), end = vertBuffer.end(); i != end; ++i)
glDeleteBuffers(1, &*i);
for(auto i = indBuffer.begin(), end = indBuffer.end(); i != end; ++i)
glDeleteBuffers(1, &*i);
delete[] shaderBuffer;
}
void VertexBufferTCV::flushRetainingLast(FlushCause reason) {
RenderStep step = *last;
float* prevBuffer = pNextShaderBuffer;
//We may have added a step that we decided we can't render, we just pop the empty step
if(step.vertEnd == 0)
steps.pop_back();
draw(reason);
step.vertStart = 0;
step.vertEnd = 0;
step.indexOffset = 0;
if(step.shaderCache) {
memcpy(pNextShaderBuffer, step.shaderCache, (prevBuffer - step.shaderCache) * sizeof(float));
auto count = prevBuffer - step.shaderCache;
step.shaderCache = pNextShaderBuffer;
pNextShaderBuffer += count;
}
steps.push_back(step);
last = &steps.back();
}
void VertexBufferTCV::duplicateLast() {
if(steps.size() != steps.capacity()) {
steps.push_back(*last);
last = &steps.back();
last->indexOffset = (unsigned short)indices.size();
last->vertStart = (unsigned short)vertices.size();
}
else {
flushRetainingLast(FC_StepLimit);
}
}
VertexTCV* VertexBufferTCV::request(unsigned polygons, unsigned polyType) {
unsigned indexCount = 0;
unsigned vertCount = 0;
bool isLines = getRequestSize(indexCount, vertCount, polygons, polyType);
unsigned prevVerts = (unsigned)vertices.size();
if(prevVerts + vertCount > maxVerts
|| indices.size() + indexCount > maxInds)
{
flushRetainingLast(FC_VertexLimit);
last->lines = isLines;
prevVerts = 0;
}
if(last->lines == isLines) {
vertices.resize(prevVerts + vertCount);
buildIndices(indices, indexCount, prevVerts, polygons, polyType);
if(last->vertEnd)
last->vertEnd += vertCount;
else
last->vertEnd = last->vertStart + vertCount - 1;
return &vertices[prevVerts];
}
else if(last->vertEnd != 0) {
duplicateLast();
last->lines = isLines;
vertices.resize(prevVerts + vertCount);
buildIndices(indices, indexCount, prevVerts, polygons, polyType);
last->vertEnd = last->vertStart + vertCount - 1;
return &vertices[prevVerts];
}
else {
last->lines = isLines;
vertices.resize(prevVerts + vertCount);
buildIndices(indices, indexCount, prevVerts, polygons, polyType);
last->vertEnd = last->vertStart + vertCount - 1;
return &vertices[prevVerts];
}
}
void VertexBufferTCV::draw(FlushCause reason) {
if(vertices.empty())
return;
if(vertBuffer.empty()) {
GLuint buffs[24];
glGenBuffers(24, buffs);
for(unsigned i = 0; i < 12; ++i)
vertBuffer.push_back(buffs[i]);
for(unsigned i = 12; i < 24; ++i)
indBuffer.push_back(buffs[i]);
}
{
lastRenderedMesh = reinterpret_cast<const render::RenderMesh*>(this);
if(useVertexArrays())
glBindVertexArray(0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indBuffer.front());
glBindBuffer(GL_ARRAY_BUFFER, vertBuffer.front());
indBuffer.push_back(indBuffer.front());
indBuffer.pop_front();
vertBuffer.push_back(vertBuffer.front());
vertBuffer.pop_front();
if(!useVertexArrays()) {
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);
glDisableVertexAttribArray(3);
glDisableVertexAttribArray(4);
glDisableVertexAttribArray(5);
}
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, sizeof(VertexTCV), (void*)offsetof(VertexTCV, uv));
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(VertexTCV), (void*)offsetof(VertexTCV, col));
glVertexPointer(3, GL_FLOAT, sizeof(VertexTCV), (void*)offsetof(VertexTCV, pos));
}
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned short),
&indices[0], GL_STREAM_DRAW);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(VertexTCV), &vertices[0], GL_STREAM_DRAW);
drawnSteps += (unsigned)steps.size();
bufferFlushes += 1;
vbFlushCounts[reason] += 1;
RenderStep* pSteps = &steps.front();
for(unsigned i = 0, cnt = (unsigned)steps.size(); i < cnt; ++i) {
auto& step = pSteps[i];
shaderUniforms = step.shaderCache;
devices.render->switchToRenderState(*step.material);
unsigned count = ((i+1) < cnt ? pSteps[i+1].indexOffset : (unsigned short)indices.size()) - step.indexOffset;
glDrawRangeElements(step.lines ? GL_LINES : GL_TRIANGLES, step.vertStart, step.vertEnd,
count, GL_UNSIGNED_SHORT, (void*)(step.indexOffset * sizeof(unsigned short)));
}
shaderUniforms = 0;
vertices.clear();
indices.clear();
steps.clear();
last = 0;
pNextShaderBuffer = shaderBuffer;
}
VertexBufferTCV* VertexBufferTCV::fetch(const RenderState* mat) {
auto& vbuff = tcv;
if(vbuff.last == 0 || vbuff.last->material != mat) {
if(vbuff.steps.size() == vbMaxSteps)
vbuff.draw(FC_StepLimit);
RenderStep step;
step.material = mat;
step.lines = false;
step.indexOffset = (unsigned short)tcv.indices.size();
step.vertStart = tcv.steps.empty() ? 0 : tcv.steps.back().vertEnd + 1;
step.vertEnd = 0;
if(mat->shader && !mat->shader->constant) {
step.shaderCache = tcv.pNextShaderBuffer;
mat->shader->saveDynamicVars(tcv.pNextShaderBuffer);
tcv.pNextShaderBuffer += mat->shader->dynamicFloats;
}
else {
step.shaderCache = 0;
}
vbuff.steps.push_back(step);
vbuff.last = &vbuff.steps.back();
}
else if(mat->shader && !mat->shader->constant) {
auto* buffer = vbuff.pNextShaderBuffer;
unsigned floats = mat->shader->dynamicFloats;
if(buffer + floats >= vbuff.shaderBuffer + vbuff.shaderBufferSize) {
//Flush the buffer and re-fetch the step
vbuff.draw(FC_ShaderLimit);
return fetch(mat);
}
else {
mat->shader->saveDynamicVars(buffer);
auto* lastStep = vbuff.last;
//When a shader uses a small enough amount of data, check to see if it's duplicated
//If it is, we can batch it into one step
if(floats > vbFloatLimit || memcmp(lastStep->shaderCache, buffer, floats * sizeof(float)) != 0) {
tcv.steps.push_back(*lastStep);
lastStep = &tcv.steps.back();
tcv.last = lastStep;
lastStep->shaderCache = buffer;
lastStep->indexOffset = (unsigned short)tcv.indices.size();
lastStep->vertStart = lastStep->vertEnd + 1;
lastStep->vertEnd = 0;
vbuff.pNextShaderBuffer += floats;
}
}
}
return &vbuff;
}
};
+145
View File
@@ -0,0 +1,145 @@
#include "lighting.h"
#include "compat/gl.h"
#include <set>
namespace render {
namespace light {
PointLight::PointLight() : att_constant(1), att_linear(0), att_quadratic(0) {
}
float PointLight::distanceFrom(const vec3f& pos) {
return (float)pos.distanceToSQ(position);
}
void setPosition(const vec3f& pos);
void setPosition(const vec3d& pos);
void PointLight::enable(unsigned& lightIndex, const vec3f& offset) {
GLenum LIGHT = GL_LIGHT0 + lightIndex; ++lightIndex;
glEnable(LIGHT);
glLightfv(LIGHT,GL_DIFFUSE,(GLfloat*)&diffuse);
glLightfv(LIGHT,GL_SPECULAR,(GLfloat*)&specular);
glLightf(LIGHT,GL_QUADRATIC_ATTENUATION,att_quadratic);
float pos[4] = {position.x + offset.x, position.y + offset.y, position.z + offset.z, 1.f};
glLightfv(LIGHT,GL_POSITION,(GLfloat*)&pos);
}
vec3f PointLight::getPosition() const {
return position;
}
float PointLight::getRadius() const {
return radius;
}
NodePointLight::NodePointLight(scene::Node* follow) : followNode(follow) {
follow->grab();
}
NodePointLight::~NodePointLight() {
if(followNode)
followNode->drop();
followNode = nullptr;
}
vec3f NodePointLight::getPosition() const {
if(!followNode)
return position;
return vec3f(followNode->abs_position);
}
float NodePointLight::getRadius() const {
if(!followNode)
return radius;
return followNode->abs_scale;
}
//TODO: This is probably out of sync by one frame
void NodePointLight::enable(unsigned& lightIndex, const vec3f& offset) {
if(!followNode) {
lightIndex += 1;
return;
}
if(followNode->queuedDelete || !followNode->parent) {
followNode->drop();
followNode = nullptr;
lightIndex += 1;
return;
}
const auto& v = followNode->abs_position;
position.x = (float)v.x;
position.y = (float)v.y;
position.z = (float)v.z;
PointLight::enable(lightIndex, offset);
}
std::set<LightSource*> lights;
void registerLight(LightSource* light) {
lights.insert(light);
}
void unregisterLight(LightSource* light) {
lights.erase(light);
}
void destroyLights() {
for(auto it = lights.begin(); it != lights.end(); ++it)
delete *it;
lights.clear();
}
void resetLights() {
Colorf diffuse, specular;
float pos[4] = {0.f, 0.f, 0.f, 1.f};
for(unsigned i = 1; i < 8; ++i)
glDisable(GL_LIGHT0 + i);
glEnable(GL_LIGHT0);
glLightfv(GL_LIGHT0, GL_DIFFUSE, (GLfloat*)&diffuse);
glLightfv(GL_LIGHT0, GL_SPECULAR, (GLfloat*)&specular);
glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, 0.f);
glLightfv(GL_LIGHT0, GL_POSITION, (GLfloat*)&pos);
}
//Finds up to <maximum> nearest lights to <from>
unsigned findNearestLights(const vec3f& from, LightSource** pLights, unsigned maximum) {
unsigned found = 0;
float distances[GL_MAX_LIGHTS];
if(maximum > GL_MAX_LIGHTS)
maximum = GL_MAX_LIGHTS;
for(auto i = lights.begin(), end = lights.end(); i != end; ++i) {
float dist = (*i)->distanceFrom(from);
for(unsigned d = 0; d < found; ++d) {
if(distances[d] > dist) {
for(unsigned x = found-1; x > d; --x) {
distances[x] = distances[x-1];
pLights[x] = pLights[x-1];
}
distances[d] = dist;
pLights[d] = *i;
goto nextLight;
}
}
if(found < maximum) {
pLights[found] = *i;
distances[found] = dist;
++found;
}
nextLight:;
}
return found;
}
};
};
+60
View File
@@ -0,0 +1,60 @@
#pragma once
#include <vector>
#include "vec3.h"
#include "color.h"
#include "scene/node.h"
namespace render {
namespace light {
struct LightSource {
//Returns a distance that can be used in comparative distance checks (not actual distance)
virtual float distanceFrom(const vec3f& pos) = 0;
//Enables the light, and advance <lightIndex>
virtual void enable(unsigned& lightIndex, const vec3f& offset) = 0;
//Get data from light
virtual vec3f getPosition() const = 0;
virtual float getRadius() const = 0;
virtual ~LightSource() {}
};
struct PointLight : public LightSource {
vec3f position;
float radius;
float att_constant, att_linear, att_quadratic;
Colorf diffuse, specular;
float distanceFrom(const vec3f& pos);
void enable(unsigned& lightIndex, const vec3f& offset);
vec3f getPosition() const;
float getRadius() const;
PointLight();
};
struct NodePointLight : public PointLight {
scene::Node* followNode;
void enable(unsigned& lightIndex, const vec3f& offset);
vec3f getPosition() const;
float getRadius() const;
NodePointLight(scene::Node* follow);
~NodePointLight();
};
void registerLight(LightSource* light);
void unregisterLight(LightSource* light);
//Removes all registered lights
void destroyLights();
//Returns lighting to its default state (a single white light at 0,0,0)
void resetLights();
//Finds up to <maximum> nearest lights to <from>
unsigned findNearestLights(const vec3f& from, LightSource** lights, unsigned maximum);
};
};
+149
View File
@@ -0,0 +1,149 @@
#include "render/obj_loader.h"
#include "compat/misc.h"
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <fstream>
#include <string>
#include <map>
namespace render {
struct VertexIndex {
int a, b, c;
VertexIndex(int A, int B, int C) : a(A), b(B), c(C) {}
bool operator<(const VertexIndex& other) const {
return memcmp(this, &other, sizeof(int) * 3) < 0;
}
};
void loadMeshOBJ(const char* filename, Mesh& mesh) {
// Read all the appropriate separate data arrays
std::vector<vec3f> vertices;
std::vector<vec3f> normals;
std::vector<float> ucoord;
std::vector<float> vcoord;
float scale = 1.f;
std::string line;
std::ifstream file(filename);
vec3f vec;
std::map<VertexIndex, unsigned> vertex_map;
if(file.is_open()) {
while(file.good()) {
std::getline(file, line);
if(line.size() == 0)
continue;
switch(line[0]) {
case 's':
if(line[1] == 'c') { //Non-standard extension 'scaling factor'
sscanf(line.c_str(), "sc %f", &scale);
}
break;
case 'v':
switch(line[1]) {
case ' ':
// Vertex
sscanf(line.c_str(), "v %f %f %f", &vec.x, &vec.y, &vec.z);
vec *= scale;
vertices.push_back(vec);
break;
case 'n':
// Normal
sscanf(line.c_str(), "vn %f %f %f", &vec.x, &vec.y, &vec.z);
normals.push_back(vec);
break;
case 't':
// Texture
float u, v;
sscanf(line.c_str(), "vt %f %f", &u, &v);
ucoord.push_back(u);
vcoord.push_back(v);
break;
}
break;
case 'f':
// Face
int vertsThisLine = 3;
int vertexIndex[4];
int uvIndex[4];
int normalIndex[4];
int elemIndex[4];
int items = sscanf(line.c_str(), "f %d/%d/%d %d/%d/%d %d/%d/%d %d/%d/%d",
&vertexIndex[0], &uvIndex[0], &normalIndex[0],
&vertexIndex[1], &uvIndex[1], &normalIndex[1],
&vertexIndex[2], &uvIndex[2], &normalIndex[2],
&vertexIndex[3], &uvIndex[3], &normalIndex[3]);
vertsThisLine = items / 3;
int vertCount = (int)vertices.size();
int normCount = (int)normals.size();
int uvCount = (int)ucoord.size();
for(int i = 0; i < vertsThisLine; ++i) {
if(vertexIndex[i] > 0)
vertexIndex[i] -= 1;
if(normalIndex[i] > 0)
normalIndex[i] -= 1;
if(uvIndex[i] > 0)
uvIndex[i] -= 1;
if(vertexIndex[i] >= vertCount || vertexIndex[i] < -vertCount)
goto ignoreFace;
if(normalIndex[i] >= normCount || normalIndex[i] < -normCount)
goto ignoreFace;
if(uvIndex[i] >= uvCount || uvIndex[i] < -uvCount)
goto ignoreFace;
if(vertexIndex[i] < 0)
vertexIndex[i] = vertCount + vertexIndex[i];
if(normalIndex[i] < 0)
normalIndex[i] = normCount + normalIndex[i];
if(uvIndex[i] < 0)
uvIndex[i] = uvCount + uvIndex[i];
VertexIndex ind(vertexIndex[i], normalIndex[i], uvIndex[i]);
auto it = vertex_map.find(ind);
if(it == vertex_map.end()) {
Vertex v;
v.position = vertices[vertexIndex[i]];
v.normal = normals[normalIndex[i]];
v.u = ucoord[uvIndex[i]];
v.v = 1.f - vcoord[uvIndex[i]];
elemIndex[i] = mesh.vertices.size();
mesh.vertices.push_back(v);
vertex_map[ind] = elemIndex[i];
}
else {
elemIndex[i] = it->second;
}
}
if(vertsThisLine >= 3)
mesh.faces.push_back(Mesh::Face(elemIndex[0], elemIndex[1], elemIndex[2]));
if(vertsThisLine >= 4)
mesh.faces.push_back(Mesh::Face(elemIndex[0], elemIndex[2], elemIndex[3]));
ignoreFace:
break;
}
}
file.close();
}
}
};
+8
View File
@@ -0,0 +1,8 @@
#pragma once
#include "mesh.h"
namespace render {
void loadMeshOBJ(const char* filename, Mesh& mesh);
};
+362
View File
@@ -0,0 +1,362 @@
#include "render/ogex_loader.h"
#include "str_util.h"
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <fstream>
#include <string>
#include <unordered_map>
#include <memory>
#include "vec2.h"
#include "matrix.h"
namespace render {
struct GEXGeometry {
std::vector<vec3f> positions, normals;
std::vector<vec2f> uvs[3];
std::vector<Colorf> colors;
std::vector<Mesh::Face> faces;
};
struct GEXNode {
Matrix transform;
std::string meshID;
std::vector<GEXNode*> nodes;
GEXNode& makeChild() {
auto* node = new GEXNode();
nodes.push_back(node);
return *node;
}
void clear() {
for(auto i = nodes.begin(), end = nodes.end(); i != end; ++i) {
(**i).clear();
delete *i;
}
}
};
struct GEXSection {
std::string id, attrib;
GEXNode* node;
};
struct StringView {
std::string str;
std::string::size_type start;
StringView() : start(0) {
}
StringView(const std::string& s) : start(0), str(s) {
}
void operator=(const std::string& s) {
str = s;
start = 0;
}
std::string::size_type find(char c) {
auto x = str.find(c, start);
if(x != std::string::npos)
return x - start;
else
return x;
}
void substr(std::string& out, std::string::size_type first, std::string::size_type count) {
out.assign(str.begin() + (start + first), str.begin() + (start + first + count));
}
void advance(std::string::size_type off) {
start += off;
}
};
static std::string& extractSegment(StringView& data, std::string& out) {
out.clear();
auto start = data.find('{');
auto pos = data.find('}');
if(pos == std::string::npos || start == std::string::npos || start > pos)
return out;
data.substr(out, start+1,pos-(start+1));
data.advance(pos+1);
return out;
}
void loadMeshOGEX(const char* filename, Mesh& mesh) {
std::string line, segment;
StringView data;
std::ifstream file(filename);
if(!file.is_open())
return;
unsigned row = 0;
GEXSection section;
GEXNode root;
GEXNode* node = &root;
std::stack<GEXSection> sections;
std::unordered_map<std::string, std::unique_ptr<GEXGeometry>> parts;
GEXGeometry* part = nullptr;
char name[129], buffer[129];
std::function<bool(std::string&)> innerParse;
while(file.good()) {
std::getline(file, line);
line = trim(line);
if(line.size() == 0)
continue;
data = line;
if(line == "{") {
sections.push(section);
section.id.clear();
section.attrib.clear();
section.node = nullptr;
}
else if(line == "}") {
if(!sections.empty())
sections.pop();
if(sections.empty())
node = &root;
else if(sections.top().node)
node = sections.top().node;
innerParse = nullptr;
}
else if(line == "float[16]" || line == "float[3]" || line == "float[2]") {
//Simplify parsing
}
else if(innerParse) {
if(innerParse(line))
innerParse = nullptr;
}
else if(sections.size() >= 0) {
std::string head, tail;
if(line[0] != '{' && splitKeyValue(line, head, tail, " ") && !tail.empty()) {
StringView value(tail);
//Parse x {...}, x (...) {...}, and x $...\n{
if(tail[0] == '{') {
if(head == "ObjectRef") {
if(tail.front() == '{' && tail.back() == '}')
tail = tail.substr(1, tail.size()-2);
value.advance(1);
auto id = trim(extractSegment(value, segment), "\"$");
if(!id.empty() && node)
node->meshID = id;
}
}
else if(tail[0] == '$') {
if(sscanf(tail.c_str(), "$%128s", buffer) == 1)
section.attrib = buffer;
section.id = head;
if(head == "Node" || head == "GeometryNode") {
node = &node->makeChild();
section.node = node;
}
}
else if(tail[0] == '(') {
if(sscanf(tail.c_str() + 1, "attrib = \"%128s", buffer) == 1)
section.attrib = trim(std::string(buffer), "\")");
if(head == "Mesh") {
section.id = "Mesh";
part = new GEXGeometry();
parts[sections.top().attrib].reset(part);
}
else if(head == "VertexArray") {
section.id = head;
if(section.attrib == "position") {
innerParse = [&](std::string& line) -> bool {
std::vector<std::string> values;
extractSegment(data, segment);
while(!segment.empty()) {
split(segment, values, ",", true);
if(values.size() == 3)
part->positions.push_back(vec3f(atof(values[0].c_str()), atof(values[1].c_str()), atof(values[2].c_str())));
values.clear();
extractSegment(data, segment);
}
return false;
};
}
else if(section.attrib == "normal") {
innerParse = [&](std::string& line) -> bool {
std::vector<std::string> values;
extractSegment(data, segment);
while(!segment.empty()) {
split(segment, values, ",", true);
if(values.size() == 3)
part->normals.push_back(vec3f(atof(values[0].c_str()), atof(values[1].c_str()), atof(values[2].c_str())));
values.clear();
extractSegment(data, segment);
}
return false;
};
}
else if(section.attrib == "texcoord" || section.attrib == "texcoord[1]" || section.attrib == "texcoord[2]") {
int index = 0;
if(section.attrib == "texcoord")
index = 0;
else if(section.attrib == "texcoord[1]")
index = 1;
else if(section.attrib == "texcoord[2]")
index = 2;
innerParse = [&,index](std::string& line) -> bool {
std::vector<std::string> values;
extractSegment(data, segment);
while(!segment.empty()) {
split(segment, values, ",", true);
if(values.size() == 2) {
vec2f uv = vec2f(atof(values[0].c_str()), atof(values[1].c_str()));
part->uvs[index].push_back(uv);
}
values.clear();
extractSegment(data, segment);
}
return false;
};
}
else if(section.attrib == "color") {
innerParse = [&](std::string& line) -> bool {
std::vector<std::string> values;
extractSegment(data, segment);
while(!segment.empty()) {
split(segment, values, ",", true);
if(values.size() == 3)
part->colors.push_back(Colorf(atof(values[0].c_str()), atof(values[1].c_str()), atof(values[2].c_str())));
values.clear();
extractSegment(data, segment);
}
return false;
};
}
}
else if(head == "IndexArray") {
section.id = head;
goto readIndexArray;
}
}
}
else if(line == "IndexArray") {
section.id = "IndexArray";
readIndexArray:
innerParse = [&](std::string& line) -> bool {
std::vector<std::string> values;
extractSegment(data, segment);
while(!segment.empty()) {
split(segment, values, ",", true);
if(values.size() == 3)
part->faces.push_back(Mesh::Face(atoi(values[0].c_str()), atoi(values[1].c_str()), atoi(values[2].c_str())));
values.clear();
extractSegment(data, segment);
}
return false;
};
}
else if(line == "Transform") {
row = 0;
innerParse = [&](std::string& line) -> bool {
if(!node)
return true;
line = trim(line, "{}");
std::vector<std::string> values;
split(line, values, ",", true);
if(values.size() == 4)
for(unsigned i = 0; i < 4; ++i)
node->transform[row*4 + i] = toNumber<double>(values[i]);
++row;
return false;
};
}
}
}
std::function<void(GEXNode&,Matrix)> processNode;
auto process = [&](GEXNode& n, Matrix transform) {
Matrix mat = n.transform * transform;
auto p = parts.find(n.meshID);
if(p != parts.end()) {
auto& part = *p->second.get();
unsigned short indexBase = mesh.vertices.size();
for(size_t i = 0; i < part.positions.size(); ++i) {
Vertex vert;
vert.position = mat * part.positions[i];
if(i < part.normals.size())
vert.normal = mat.rotate(part.normals[i]);
else
vert.normal = vert.position.normalized();
if(i < part.uvs[0].size()) {
vert.u = part.uvs[0][i].x;
vert.v = part.uvs[0][i].y;
}
mesh.vertices.push_back(vert);
//Optional second/thid uv
{
auto& uvs = part.uvs[1];
auto& uvs2 = part.uvs[2];
if(i < uvs.size() || i < uvs2.size()) {
if(mesh.uvs2.size() < indexBase)
mesh.uvs2.resize(indexBase);
vec4f uv;
if(i < uvs.size()) {
uv.x = uvs[i].x;
uv.y = uvs[i].y;
}
if(i < uvs2.size()) {
uv.z = uvs2[i].x;
uv.w = uvs2[i].y;
}
mesh.uvs2.push_back(uv);
}
}
//Optional vertex color
if(i < part.colors.size()) {
if(mesh.colors.size() < indexBase)
mesh.colors.resize(indexBase);
mesh.colors.push_back(part.colors[i]);
}
}
for(size_t i = 0; i < part.faces.size(); ++i) {
Mesh::Face face = part.faces[i];
face.a += indexBase;
face.b += indexBase;
face.c += indexBase;
if(face.a < mesh.vertices.size() && face.b < mesh.vertices.size() && face.c < mesh.vertices.size())
mesh.faces.push_back(face);
}
}
for(auto i = n.nodes.begin(), end = n.nodes.end(); i != end; ++i)
processNode(**i, mat);
};
processNode = process;
process(root, Matrix());
root.clear();
}
}
+8
View File
@@ -0,0 +1,8 @@
#pragma once
#include "mesh.h"
namespace render {
void loadMeshOGEX(const char* filename, Mesh& mesh);
};
+22
View File
@@ -0,0 +1,22 @@
#pragma once
#include "aabbox.h"
struct Mesh;
namespace render {
class RenderMesh {
public:
virtual void resetToMesh(const Mesh& mesh) = 0;
virtual const RenderMesh* selectLOD(double distance) const = 0;
virtual void setLOD(double distance, const RenderMesh* mesh) = 0;
virtual const AABBoxf& getBoundingBox() const = 0;
virtual const Mesh& getMesh() const = 0;
virtual unsigned getMeshBytes() const = 0;
virtual void render() const = 0;
virtual ~RenderMesh() {}
};
};
+29
View File
@@ -0,0 +1,29 @@
#include "render_state.h"
#include "memory/AllocOnlyPool.h"
#include "threads.h"
#include <string.h>
namespace render {
memory::AllocOnlyPool<RenderState,threads::Mutex> renderStatePool(256);
RenderState::RenderState() : culling(FC_Back),
depthTest(DT_Less), depthWrite(true), lighting(true),
normalizeNormals(false), baseMat(MAT_Solid), drawMode(DM_Fill),
wrapHorizontal(TW_Repeat), wrapVertical(TW_Repeat),
filterMin(TF_Linear), filterMag(TF_Linear),
mipmap(true), cachePixels(false),
diffuse(1,1,1,1), specular(1,1,1,1), shininess(3.f), textures(), shader(0), constant(true)
{
memset(textures, 0, sizeof(textures));
}
void* RenderState::operator new(size_t size) {
return renderStatePool.alloc();
}
void RenderState::operator delete(void* p) {
renderStatePool.dealloc((RenderState*)p);
}
};
+101
View File
@@ -0,0 +1,101 @@
#pragma once
#include "compat/misc.h"
#include "render/shader.h"
#include "color.h"
#include <string>
#ifndef RENDER_MAX_TEXTURES
#define RENDER_MAX_TEXTURES 8
#endif
namespace render {
class Texture;
unsigned_enum(FaceCulling) {
FC_None,
FC_Front,
FC_Back,
FC_Both
};
unsigned_enum(DepthTest) {
DT_Never,
DT_Less,
DT_Equal,
DT_LessEqual,
DT_Greater,
DT_NotEqual,
DT_GreaterEqual,
DT_Always,
//A depth test that always passes is equivalent to no test
DT_NoDepthTest = DT_Always,
};
unsigned_enum(TextureWrap) {
TW_Repeat,
TW_Clamp,
TW_ClampEdge,
TW_Mirror
};
unsigned_enum(TextureFilter) {
TF_Nearest,
TF_Linear
};
unsigned_enum(BaseMaterial) {
MAT_Solid,
MAT_Add,
MAT_Alpha,
MAT_Font,
MAT_Overlay,
};
unsigned_enum(DrawMode) {
DM_Fill,
DM_Line,
};
//Holds the target render state
//=====
//Notes:
// Alpha test is always >0
// Normalize Normals requires uniform scaling
struct RenderState {
FaceCulling culling : 2;
DepthTest depthTest : 4;
BaseMaterial baseMat : 3;
DrawMode drawMode : 2;
bool depthWrite : 1;
bool lighting : 1;
bool normalizeNormals : 1;
TextureWrap wrapHorizontal : 2;
TextureWrap wrapVertical : 2;
TextureFilter filterMin : 2;
TextureFilter filterMag : 2;
bool mipmap : 1;
bool cachePixels : 1;
bool constant : 1;
Colorf diffuse, specular;
float shininess;
Texture* textures[RENDER_MAX_TEXTURES];
const Shader* shader;
void* operator new(size_t size);
void operator delete(void* p);
RenderState();
};
struct MaterialGroup {
std::string prefix;
RenderState base;
std::vector<RenderState*> materials;
std::vector<std::string> names;
};
};
+112
View File
@@ -0,0 +1,112 @@
#pragma once
#include <string>
#include <vector>
namespace render {
class ShaderProgram {
public:
virtual int compile() = 0;
virtual ~ShaderProgram() {}
};
class Shader {
public:
ShaderProgram* program;
enum VarType {
VT_invalid = 0,
VT_int,
VT_int2,
VT_int3,
VT_int4,
VT_float,
VT_float2,
VT_float3,
VT_float4,
VT_mat3
};
struct Variable {
VarType type : 16;
unsigned short count : 16;
union {
mutable int* _ints;
mutable float* _floats;
};
union {
mutable void* _args;
};
union {
void (*_intcall)(int*,unsigned short,void*);
void (*_floatcall)(float*,unsigned short,void*);
};
bool constant;
Variable() : type(VT_invalid), count(0), _ints(0), _args(0), _intcall(0), constant(true) {}
Variable(VarType Type, unsigned short Count) : type(Type), count(Count), _args(0), _intcall(0), constant(true) {
switch(Type) {
case VT_int:
_ints = new int[Count*1]; break;
case VT_int2:
_ints = new int[Count*2]; break;
case VT_int3:
_ints = new int[Count*3]; break;
case VT_int4:
_ints = new int[Count*4]; break;
case VT_float:
_floats = new float[Count*1]; break;
case VT_float2:
_floats = new float[Count*2]; break;
case VT_float3:
_floats = new float[Count*3]; break;
case VT_float4:
_floats = new float[Count*4]; break;
case VT_mat3:
_floats = new float[Count*9]; break;
case VT_invalid:
break;
}
}
};
std::vector<Variable> vars;
unsigned dynamicFloats;
//Whether the shader's inputs are constant across a frame
bool constant;
virtual int compile() = 0;
virtual void addVariable(const std::string& name, const Variable& var) {
vars.push_back(var);
if(!var.constant) {
constant = false;
switch(var.type) {
case VT_int:
case VT_float:
dynamicFloats += var.count; break;
case VT_int2:
case VT_float2:
dynamicFloats += 2 * var.count; break;
case VT_int3:
case VT_float3:
dynamicFloats += 3 * var.count; break;
case VT_int4:
case VT_float4:
dynamicFloats += 4 * var.count; break;
case VT_invalid:
break;
}
}
}
virtual void bind(float* dynamicBuffer) const = 0;
virtual void updateDynamicVars() const = 0;
virtual void saveDynamicVars(float* buffer) const = 0;
virtual void loadDynamicVars(float* buffer) const = 0;
virtual ~Shader() {}
};
};
+101
View File
@@ -0,0 +1,101 @@
#include "obj/object.h"
#include "scene/node.h"
#include "main/references.h"
#include "main/logging.h"
#include <map>
struct StateVar {
ScriptObjectType* type;
unsigned offset;
};
std::map<std::string,unsigned> vars;
std::vector<StateVar> stateVars;
unsigned registerVar(const std::string& name) {
auto entry = vars.find(name);
if(entry == vars.end()) {
unsigned index = (unsigned)vars.size();
vars[name] = index;
return index;
}
else {
return entry->second;
}
}
void prepareShaderStateVars() {
stateVars.resize(vars.size());
for(auto i = vars.begin(), end = vars.end(); i != end; ++i) {
StateVar& var = stateVars[i->second];
var.type = 0;
auto pos = i->first.find("::");
if(pos == std::string::npos) {
error("Shader State Variable: '%s' is not a state (should be ObjectType::MemberName)", i->first.c_str());
continue;
}
std::string objName = i->first.substr(0,pos), memberName = i->first.substr(pos+2);
ScriptObjectType* type = getScriptObjectType(objName);
if(type == 0) {
error("Shader State Variable: '%s' is not an object type (from '%s')", objName.c_str(), i->first.c_str());
continue;
}
bool memberFound = false;
//Locate the member, and make sure it is a double
const StateDefinition& states = *type->states;
unsigned base = sizeof(Object);
states.align(base);
for(unsigned j = 0; j < states.types.size(); ++j) {
auto& member = states.types[j];
if(member.name == memberName) {
memberFound = true;
if(member.def->type == "double") {
var.offset = base + member.offset;
var.type = type;
}
else {
error("Shader State Variable: Only doubles are supported");
}
break;
}
}
if(!memberFound) {
error("Shader State Variable: %s has no member of type '%s'", objName.c_str(), memberName.c_str());
}
}
}
void clearShaderStateVars() {
stateVars.clear();
vars.clear();
}
void shader_statevars(float* out, unsigned short n, void* args) {
if(!scene::renderingNode)
return;
Object* obj = scene::renderingNode->obj;
if(obj) {
int* indices = (int*)args;
char* base = (char*)obj;
for(unsigned short i = 0; i < n; ++i) {
const StateVar& var = stateVars[indices[i]];
if(obj->type == var.type)
out[i] = (float)*(double*)(base + var.offset);
else
out[i] = 0;
}
}
else {
for(unsigned short i = 0; i < n; ++i)
out[i] = 0;
}
}
+185
View File
@@ -0,0 +1,185 @@
#include "render/spritesheet.h"
void shader_sprite_pos(float* output, unsigned short,void*) {
if(render::SpriteSheet::current) {
vec4f pos = render::SpriteSheet::current->getSourceUV(render::SpriteSheet::currentIndex);
output[0] = pos.x;
output[1] = pos.y;
output[2] = pos.z;
output[3] = pos.w;
}
else {
output[0] = 0.f;
output[1] = 0.f;
output[2] = 1.f;
output[3] = 1.f;
}
}
namespace render {
const SpriteSheet* SpriteSheet::current = nullptr;
unsigned SpriteSheet::currentIndex = 0;
SpriteSheet::SpriteSheet()
: mode(SM_Horizontal), width(32), height(32), spacing(0), perLine(0), dirty(true) {
}
inline vec2i getXY(const SpriteSheet& sheet, unsigned index) {
if(sheet.dirty) {
auto* texture = sheet.material.textures[0];
if(!texture) {
sheet.dirty = false;
sheet.perLine = 0;
return vec2i();
}
else if(!texture->loaded) {
return vec2i();
}
sheet.dirty = false;
sheet.perLine = 0;
if(!texture)
return vec2i();
if(sheet.mode == SM_Horizontal) {
unsigned imgWidth = texture->size.width;
if(imgWidth != 0)
sheet.perLine = (imgWidth - sheet.spacing) / (sheet.width + sheet.spacing);
}
else { //SM_Vertical:
unsigned imgHeight = texture->size.height;
if(imgHeight != 0)
sheet.perLine = (imgHeight - sheet.spacing) / (sheet.height + sheet.spacing);
}
}
if(sheet.mode == SM_Horizontal) {
int perRow = sheet.perLine;
if(perRow != 0)
return vec2i(index % perRow, index / perRow);
}
else { //SM_Vertical
int perCol = sheet.perLine;
if(perCol != 0)
return vec2i(index / perCol, index % perCol);
}
return vec2i();
}
vec4f SpriteSheet::getSourceUV(unsigned index) const {
if(material.textures[0]) {
const vec2i imgSize = material.textures[0]->size;
if(imgSize.width == 0 || imgSize.height == 0)
return vec4f();
switch(mode) {
case SM_Horizontal: {
unsigned perRow = (imgSize.width - spacing) / (width + spacing);
float x = float((index % perRow) * (width + spacing) + spacing) / float(imgSize.width);
float y = float((index / perRow) * (height + spacing) + spacing) / float(imgSize.height);
return vec4f(x,y,x + float(width)/float(imgSize.width), y + float(height)/float(imgSize.height));
} break;
case SM_Vertical: {
unsigned perCol = (imgSize.height - spacing) / (height + spacing);
float x = float((index / perCol) * (width + spacing) + spacing) / float(imgSize.width);
float y = float((index % perCol) * (height + spacing) + spacing) / float(imgSize.height);
return vec4f(x,y,x + float(width)/float(imgSize.width), y + float(height)/float(imgSize.height));
} break;
}
}
return vec4f(0,0,1.f,1.f);
}
bool SpriteSheet::isPixelActive(unsigned index, const vec2i& px) const {
recti src = getSource(index);
if(!src.isWithin(px + src.topLeft))
return false;
auto* tex = material.textures[0];
if(!tex)
return false;
return tex->isPixelActive(src.topLeft + px);
}
recti SpriteSheet::getSource(unsigned index) const {
vec2i pos = getXY(*this, index);
recti source;
source.topLeft.x = pos.x * (width + spacing) + spacing;
source.topLeft.y = pos.y * (height + spacing) + spacing;
source.botRight.x = source.topLeft.x + width;
source.botRight.y = source.topLeft.y + height;
return source;
}
void SpriteSheet::getSource(unsigned index, vec2f* out) const {
vec2i pos = getXY(*this, index);
out[0].x = (float)(pos.x * (width + spacing) + spacing);
out[0].y = (float)(pos.y * (height + spacing) + spacing);
out[2].x = out[0].x + width;
out[2].y = out[0].y + height;
out[1].x = out[2].x;
out[1].y = out[0].y;
out[3].x = out[0].x;
out[3].y = out[2].y;
}
unsigned SpriteSheet::getCount() const {
if(material.textures[0]) {
vec2i texSize = material.textures[0]->size;
if(texSize.width == 0 || texSize.height == 0)
return 0;
return (texSize.width / width) * (texSize.height / height);
}
else {
return 0;
}
}
void SpriteSheet::render(RenderDriver& driver, unsigned index, const vec3d* vertices, const Color* color) const {
current = this;
currentIndex = index;
vec2f texcoords[4];
getSource(index, texcoords);
driver.switchToRenderState(material);
driver.drawQuad(vertices, texcoords, color);
current = nullptr;
}
void SpriteSheet::render(RenderDriver& driver, unsigned index, recti rectangle, const Color* color, const recti* clip, const Shader* shader, double rotation) const {
current = this;
currentIndex = index;
recti source = getSource(index);
if(shader) {
static render::RenderState state;
state = material;
state.shader = shader;
state.constant = false;
driver.drawRectangle(rectangle, &state, &source, color, clip, rotation);
}
else {
driver.drawRectangle(rectangle, &material, &source, color, clip, rotation);
}
current = nullptr;
}
};
+57
View File
@@ -0,0 +1,57 @@
#pragma once
#include "vec2.h"
#include "vec4.h"
#include "render/driver.h"
#include "render/render_state.h"
namespace render {
enum SpriteMode {
SM_Horizontal,
SM_Vertical,
};
class SpriteSheet {
public:
static const SpriteSheet* current;
static unsigned currentIndex;
RenderState material;
SpriteMode mode;
int width, height;
int spacing;
mutable int perLine;
mutable bool dirty;
unsigned getCount() const;
SpriteSheet();
bool isPixelActive(unsigned index, const vec2i& px) const;
vec4f getSourceUV(unsigned index) const;
recti getSource(unsigned index) const;
void getSource(unsigned index, vec2f* out) const;
void render(RenderDriver& driver, unsigned index, const vec3d* vertices, const Color* color = 0) const;
void render(RenderDriver& driver, unsigned index, recti rectangle, const Color* color = 0, const recti* clip = 0, const render::Shader* shader = 0, double rotation = 0.0) const;
};
class Sprite {
public:
const SpriteSheet* sheet;
const RenderState* mat;
unsigned index;
Color color;
Sprite() : sheet(0), mat(0), index(0) {
}
Sprite(const SpriteSheet* Sheet, unsigned Index)
: sheet(Sheet), mat(0), index(Index) {
}
Sprite(const RenderState* Material)
: sheet(0), mat(Material), index(0) {
}
};
};
+38
View File
@@ -0,0 +1,38 @@
#pragma once
#include "vec2.h"
#include "image.h"
#include "rect.h"
#include "render_state.h"
namespace render {
enum TextureType {
TT_2D,
TT_Cubemap
};
class Texture {
public:
TextureType type;
vec2i size;
bool loaded;
bool hasMipMaps;
mutable RenderState prevRenderState;
virtual bool isPixelActive(vec2i px) const = 0;
virtual void loadStart(Image& image, bool mipmap = true, bool cachePixels = false, unsigned lod = 0) = 0;
virtual void loadPartial(Image& image, const recti& pixels, bool cachePixels = false, unsigned lod = 0) = 0;
virtual void loadFinish(bool mipmap, unsigned lod = 0) = 0;
virtual void load(Image& image, bool mipmap = true, bool cachePixels = false) = 0;
virtual void save(Image& image) const = 0;
virtual void bind() = 0;
virtual unsigned getID() const = 0;
virtual unsigned getTextureBytes() const = 0;
virtual ~Texture() {}
};
};
+105
View File
@@ -0,0 +1,105 @@
#pragma once
#include "vec3.h"
#include "vec2.h"
#include "color.h"
#include <vector>
#include <deque>
namespace scene {
class Node;
};
namespace render {
struct RenderState;
extern unsigned vbFloatLimit, vbMaxSteps;
enum FlushCause {
FC_Forced,
FC_VertexLimit,
FC_StepLimit,
FC_ShaderLimit,
FC_COUNT
};
extern unsigned vbFlushCounts[FC_COUNT];
struct RenderStep {
const RenderState* material;
float* shaderCache;
unsigned short indexOffset;
unsigned short vertStart, vertEnd;
bool lines;
};
struct VertexTCV {
vec2f uv;
Color col;
vec3f pos;
inline void set(const vec3f& Pos) {
col = Color();
uv = vec2f();
pos = Pos;
}
inline void set(const vec2f& UV) {
col = Color();
uv = UV;
pos = vec3f();
}
inline void set(Color Col) {
col = Col;
uv = vec2f();
pos = vec3f();
}
inline void set(const vec3f& Pos, const vec2f& UV) {
col = Color();
uv = UV;
pos = Pos;
}
inline void set(const vec3f& Pos, const vec2f& UV, Color Col) {
col = Col;
uv = UV;
pos = Pos;
}
};
struct VertexCV {
Color col;
vec3f pos;
};
struct VertexTV {
vec2f uv;
vec3f pos;
};
class VertexBufferTCV {
RenderStep* last;
std::deque<unsigned> vertBuffer, indBuffer;
std::vector<VertexTCV> vertices;
std::vector<unsigned short> indices;
std::vector<RenderStep> steps;
float* shaderBuffer, *pNextShaderBuffer;
unsigned shaderBufferSize;
void flushRetainingLast(FlushCause reason);
void duplicateLast();
public:
static VertexBufferTCV* fetch(const RenderState* mat);
VertexTCV* request(unsigned polygons, unsigned polyType);
void draw(FlushCause reason = FC_Forced);
VertexBufferTCV();
~VertexBufferTCV();
};
//Renders out all pending vertex buffers
void renderVertexBuffers();
};
+239
View File
@@ -0,0 +1,239 @@
#include "render/x_loader.h"
#include <stdio.h>
#include <string>
#include <fstream>
#include <vector>
#include <deque>
#include "matrix.h"
#include "vec2.h"
#include "mesh.h"
#include "str_util.h"
namespace render {
bool startsWith(const std::string& str, const std::string& start) {
return str.compare(0, start.size(), start) == 0;
}
void loadXNormals(std::ifstream& file, std::vector<unsigned>& normIndices, std::vector<vec3f>& normals, Matrix& matrix) {
std::string line;
std::getline(file, line);
unsigned norms = 0;
sscanf(line.c_str(), " %u", &norms);
normals.reserve(norms);
//Normals
for(unsigned i = 0; i < norms; ++i) {
std::getline(file, line);
vec3d norm;
sscanf(line.c_str(), " %lf;%lf;%lf;,", &norm.x, &norm.y, &norm.z);
normals.push_back(vec3f(matrix.rotate(norm).normalized()));
}
std::getline(file, line);
unsigned faces = 0;
sscanf(line.c_str(), " %u", &faces);
normIndices.reserve(norms * 4);
//Face normals
for(unsigned i = 0; i < faces; ++i) {
std::getline(file, line);
unsigned type, indices[4];
auto args = sscanf(line.c_str(), " %u;%u,%u,%u,%u", &type, &indices[0], &indices[1], &indices[2], &indices[3]);
if(args >= 4) {
normIndices.push_back(indices[0]);
normIndices.push_back(indices[1]);
normIndices.push_back(indices[2]);
if(args == 5) {
normIndices.push_back(indices[0]);
normIndices.push_back(indices[2]);
normIndices.push_back(indices[3]);
}
}
}
do {
std::getline(file, line);
} while(file.good() && line.find('}') == std::string::npos);
}
void loadXUVs(std::ifstream& file, std::vector<vec2f>& uvs) {
std::string line;
std::getline(file, line);
unsigned uvCount = 0;
sscanf(line.c_str(), " %u", &uvCount);
uvs.reserve(uvCount);
//Normals
for(unsigned i = 0; i < uvCount; ++i) {
std::getline(file, line);
vec2f uv;
sscanf(line.c_str(), " %f;%f;,", &uv.x, &uv.y);
uvs.push_back(uv);
}
do {
std::getline(file, line);
} while(file.good() && line.find('}') == std::string::npos);
}
void loadXMesh(std::ifstream& file, Mesh& mesh, Matrix& matrix) {
std::vector<vec2f> uvs;
std::vector<vec3f> positions, normals;
std::vector<unsigned> posIndices, normIndices;
std::string line;
std::getline(file, line);
unsigned verts = 0;
sscanf(line.c_str(), " %u", &verts);
positions.reserve(verts);
//Vertices
for(unsigned i = 0; i < verts; ++i) {
std::getline(file, line);
vec3d vert;
sscanf(line.c_str(), " %lf;%lf;%lf;,", &vert.x, &vert.y, &vert.z);
positions.push_back(vec3f(matrix * vert));
}
std::getline(file, line);
unsigned faces = 0;
sscanf(line.c_str(), " %u", &faces);
posIndices.reserve(verts * 4);
//Faces
for(unsigned i = 0; i < faces; ++i) {
std::getline(file, line);
unsigned type, indices[4];
auto args = sscanf(line.c_str(), " %u;%u,%u,%u,%u", &type, &indices[0], &indices[1], &indices[2], &indices[3]);
if(args >= 4) {
posIndices.push_back(indices[0]);
posIndices.push_back(indices[1]);
posIndices.push_back(indices[2]);
if(args == 5) {
posIndices.push_back(indices[0]);
posIndices.push_back(indices[2]);
posIndices.push_back(indices[3]);
}
}
}
//Load normals and texture coords
std::getline(file, line);
do {
line = trim(line, " \t");
if(startsWith(line, "MeshNormals")) {
loadXNormals(file, normIndices, normals, matrix);
}
else if(startsWith(line, "MeshTextureCoords")) {
loadXUVs(file, uvs);
}
else if(line.find('{') != std::string::npos) {
//Skip unknown { ... } blocks
unsigned depth = 1;
while(depth > 0 && file.good()) {
std::getline(file, line);
auto pos = line.find_first_of("{}");
if(pos == std::string::npos)
continue;
if(line[pos] == '{')
++depth;
else
--depth;
}
}
std::getline(file, line);
} while(file.good() && line.find('}') == std::string::npos);
for(unsigned int i = 0; i + 2 < posIndices.size() && i + 2 < normIndices.size(); i += 3) {
Vertex verts[3];
for(unsigned v = 0; v < 3; ++v) {
unsigned pInd = posIndices[i+v], nInd = normIndices[i+v];
if(pInd >= positions.size() || nInd >= normals.size())
return; //Out of bounds on our inputs, return what we've gotten so far
verts[v].position = positions[pInd];
verts[v].normal = normals[nInd];
if(pInd < uvs.size()) {
verts[v].u = uvs[pInd].x;
verts[v].v = uvs[pInd].y;
}
}
unsigned short base = (unsigned short)mesh.vertices.size();
mesh.vertices.push_back(verts[0]);
mesh.vertices.push_back(verts[1]);
mesh.vertices.push_back(verts[2]);
mesh.faces.push_back(Mesh::Face(base, base+1, base+2));
}
}
void loadXFrame(std::ifstream& file, Mesh& mesh, Matrix* baseMatrix = 0) {
Matrix matrix;
if(baseMatrix)
matrix = *baseMatrix;
while(file.good()) {
std::string line;
std::getline(file, line);
line = trim(line, " \t");
if(line.empty())
continue;
if(startsWith(line, "FrameTransformMatrix")) {
std::getline(file, line);
Matrix lineMatrix;
sscanf(line.c_str(), " %lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf;;",
&lineMatrix[0], &lineMatrix[1], &lineMatrix[2], &lineMatrix[3],
&lineMatrix[4], &lineMatrix[5], &lineMatrix[6], &lineMatrix[7],
&lineMatrix[8], &lineMatrix[9], &lineMatrix[10], &lineMatrix[11],
&lineMatrix[12], &lineMatrix[13], &lineMatrix[14], &lineMatrix[15] );
if(baseMatrix)
matrix = *baseMatrix * lineMatrix;
else
matrix = lineMatrix;
std::getline(file, line); //Read the } line off
}
else if(startsWith(line, "Frame")) {
loadXFrame(file, mesh, &matrix);
}
else if(startsWith(line, "Mesh")) {
loadXMesh(file, mesh, matrix);
}
else if(line.find('}') != std::string::npos) {
return;
}
}
}
void loadMeshX(const char* filename, Mesh& mesh) {
std::ifstream file(filename);
if(file.is_open()) {
while(file.good()) {
std::string line;
std::getline(file, line);
line = trim(line, " \t");
if(line.empty())
continue;
if(startsWith(line, "Frame"))
loadXFrame(file, mesh);
}
}
}
};
+9
View File
@@ -0,0 +1,9 @@
#pragma once
struct Mesh;
namespace render {
void loadMeshX(const char* filename, Mesh& mesh);
};