Initial Commit

This commit is contained in:
n00b87
2024-02-10 11:27:02 -06:00
parent a749abe795
commit 692f8f342e
31 changed files with 32686 additions and 0 deletions

5653
rcbasic_runtime/main.cpp Normal file

File diff suppressed because it is too large Load Diff

1417
rcbasic_runtime/rc_defines.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,822 @@
#include "rc_matrix.h"
#include <cmath>
#ifndef RC_GEOMETRY_H_INCLUDED
#define RC_GEOMETRY_H_INCLUDED
// ------------------ 3D Transform Routines --------------------------------
double CalculateFaceZ(double cam_dist, double graph_offset_x, double graph_offset_y, double view_w, double view_h, double view_depth, uint32_t mA, int f_vertex_count, double* columns, double* face_min_z, double* face_max_z, double* z_avg)
{
bool dbg = false;
if(f_vertex_count > 4)
return -1;
double vx, vy, vz[4];
int oi_count = 0;
double n_face_min_z = MatrixValue(mA, 2, (uint32_t)columns[0]);
double n_face_max_z = n_face_min_z;
bool in_zx_range = false;
bool in_zy_range = false;
double face_min_x = 0;
double face_max_x = 0;
double face_min_y = 0;
double face_max_y = 0;
bool zx_min_bound = false;
bool zx_max_bound = false;
bool zy_min_bound = false;
bool zy_max_bound = false;
double distance = 0;
double z_dist = 0; //C3D_CAMERA_LENS - z
double off_x = graph_offset_x;
double off_y = graph_offset_y;
double min_x = (0 - off_x) / cam_dist;
double min_y = (view_h - off_y) / cam_dist * -1;
double max_x = (view_w - off_x) / cam_dist;
double max_y = (0 - off_y) / cam_dist * -1;
double zx_min, zx_max, zy_min, zy_max;
for(int i = 0; i < f_vertex_count; i++)
{
vz[i] = MatrixValue(mA, 2, (uint32_t)columns[i]);
vx = MatrixValue(mA, 0, (uint32_t)columns[i]);
vy = MatrixValue(mA, 1, (uint32_t)columns[i]);
distance = -1*vz[i];
if(distance >= 0 && distance < view_depth)
{
double d = cam_dist - (distance*-1);
zx_min = min_x * d;
zx_max = max_x * d;
zy_min = min_y * d;
zy_max = max_y * d;
in_zx_range = in_zx_range || (vx >= zx_min && vx < zx_max);
in_zy_range = in_zy_range || (vy >= zy_min && vy < zy_max);
zx_min_bound = zx_min_bound || (vx < zx_min);
zx_max_bound = zx_max_bound || (vx >= zx_max);
zy_min_bound = zy_min_bound || (vy < zy_min);
zy_max_bound = zy_max_bound || (vy >= zy_max);
}
else if(vz[i] >= 0 && vz[i] < view_depth)
{
double d = cam_dist - (vz[i]*-1);
zx_min = min_x * d;
zx_max = max_x * d;
zy_min = min_y * d;
zy_max = max_y * d;
in_zx_range = in_zx_range || (vx >= zx_min && vx < zx_max);
in_zy_range = in_zy_range || (vy >= zy_min && vy < zy_max);
zx_min_bound = zx_min_bound || (vx < zx_min);
zx_max_bound = zx_max_bound || (vx >= zx_max);
zy_min_bound = zy_min_bound || (vy < zy_min);
zy_max_bound = zy_max_bound || (vy >= zy_max);
}
n_face_min_z = min(n_face_min_z, vz[i]);
n_face_max_z = max(n_face_max_z, vz[i]);
}
in_zx_range = in_zx_range || (zx_min_bound && zx_max_bound);
in_zy_range = in_zy_range || (zy_min_bound && zy_max_bound);
if( (!in_zx_range) || (!in_zy_range) )
return -1;
//'if key(k_i) and actor = 1 then : print "face = ";face_num : end if
z_avg[0] = (n_face_min_z+n_face_max_z) / 2; //'This is some bullshit math to order the faces with out checking if they are obscured
face_min_z[0] = n_face_min_z;
face_max_z[0] = n_face_max_z;
//C3D_Actor_Face_ZOrder[actor, face_num] = face_min_z
if(face_min_z[0] >= cam_dist)
return -1;
else
return (cam_dist - face_min_z[0]);
}
int GetLinePlaneIntersection(double* line_point, double* line_direction, double* plane_point_1, double* plane_point_2, double* plane_point_3, double* intersection)
{
//' """
//' Calculates the intersection point of a line and a plane in 3D space.
//'
//' Parameters:
//' line_point (tuple or list): a point on the line (x, y, z)
//' line_direction (tuple or list): the direction vector of the line (x, y, z)
//' plane_point_1 (tuple or list): one point on the plane (x, y, z)
//' plane_point_2 (tuple or list): another point on the plane (x, y, z)
//' plane_point_3 (tuple or list): a third point on the plane (x, y, z)
//'
//' Returns:
//' intersection (tuple): the intersection point (x, y, z), or None if the line is parallel to the plane
//' """
//' # calculate the normal vector of the plane using the cross product of two vectors on the plane
double plane_vector_1[3], plane_vector_2[3], plane_normal[3];
plane_vector_1[0] = plane_point_2[0] - plane_point_1[0];
plane_vector_1[1] = plane_point_2[1] - plane_point_1[1];
plane_vector_1[2] = plane_point_2[2] - plane_point_1[2];
plane_vector_2[0] = plane_point_3[0] - plane_point_1[0];
plane_vector_2[1] = plane_point_3[1] - plane_point_1[1];
plane_vector_2[2] = plane_point_3[2] - plane_point_1[2];
plane_normal[0] = plane_vector_1[1] * plane_vector_2[2] - plane_vector_1[2] * plane_vector_2[1];
plane_normal[1] = plane_vector_1[2] * plane_vector_2[0] - plane_vector_1[0] * plane_vector_2[2];
plane_normal[2] = plane_vector_1[0] * plane_vector_2[1] - plane_vector_1[1] * plane_vector_2[0];
//'# calculate the scalar value of t using the line equation
double t = ((plane_point_1[0] - line_point[0]) * plane_normal[0] + (plane_point_1[1] - line_point[1]) * plane_normal[1] + (plane_point_1[2] - line_point[2]) * plane_normal[2]);
//'print "t1 = ";t
t = t / (line_direction[0] * plane_normal[0] + line_direction[1] * plane_normal[1] + line_direction[2] * plane_normal[2]);
//'print "t2 = ";(line_direction[0] * plane_normal[0] + line_direction[1] * plane_normal[1] + line_direction[2] * plane_normal[2])
//'# calculate the intersection point using the line equation
intersection[0] = line_point[0] + t * line_direction[0];
intersection[1] = line_point[1] + t * line_direction[1];
intersection[2] = line_point[2] + t * line_direction[2];
//'# check if the intersection point is on the plane
double plane_distance = abs((intersection[0] - plane_point_1[0]) * plane_normal[0] + (intersection[1] - plane_point_1[1]) * plane_normal[1] + (intersection[2] - plane_point_1[2]) * plane_normal[2]);
if(plane_distance < 10^-6)
return true;
else
return false;
}
double Distance3D(double x1, double y1, double z1, double x2, double y2, double z2)
{
return sqrt( pow((x2 - x1),2) + pow((y2 - y1),2) + pow((z2 - z1),2) );
}
double Interpolate(double min_a, double max_a, double mid_a, double min_b, double max_b)
{
return ( (mid_a-min_a)/(max_a-min_a)) * (max_b-min_b) + min_b;
}
int GetLineIntersect(double p0_x, double p0_y, double p1_x, double p1_y, double p2_x, double p2_y, double p3_x, double p3_y, double* i_x, double* i_y)
{
double s1_x = p1_x - p0_x;
double s1_y = p1_y - p0_y;
double s2_x = p3_x - p2_x;
double s2_y = p3_y - p2_y;
double n = ( (-1 * s2_x) * s1_y + s1_x * s2_y);
if(n == 0)
return 0;
double s = ( (-1 * s1_y) * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / n;
double t = ( s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / n;
i_x[0] = p0_x + (t * s1_x);
i_y[0] = p0_y + (t * s1_y);
if(s >= 0 && s <= 1 && t >= 0 && t <= 1)
{
//' Collision detected
return 1;
}
//' No collision
return 0;
}
int PointInQuad(double x, double y, double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4)
{
//'"""
//'Check if a point (x, y) is inside a quadrilateral defined by its four vertices (x1, y1), (x2, y2), (x3, y3), and (x4, y4).
//'"""
//'# Compute the cross products of vectors from the point to each vertex of the quadrilateral.
//'# If all cross products have the same sign, the point is inside the quadrilateral.
double cross1 = (x - x1) * (y2 - y1) - (y - y1) * (x2 - x1);
double cross2 = (x - x2) * (y3 - y2) - (y - y2) * (x3 - x2);
double cross3 = (x - x3) * (y4 - y3) - (y - y3) * (x4 - x3);
double cross4 = (x - x4) * (y1 - y4) - (y - y4) * (x1 - x4);
if(cross1 >= 0 && cross2 >= 0 && cross3 >= 0 && cross4 >= 0)
return 1;
else if(cross1 <= 0 && cross2 <= 0 && cross3 <= 0 && cross4 <= 0)
return 1;
else
return 0;
}
int PointInTri(double x, double y, double x1, double y1, double x2, double y2, double x3, double y3)
{
//"""
//Check if a point (x, y) is inside a triangle defined by its three vertices (x1, y1), (x2, y2), and (x3, y3).
//"""
//# Calculate the barycentric coordinates of the point with respect to the triangle vertices.
double denominator = (y2 - y3) * (x1 - x3) + (x3 - x2) * (y1 - y3);
double alpha = ((y2 - y3) * (x - x3) + (x3 - x2) * (y - y3)) / denominator;
double beta = ((y3 - y1) * (x - x3) + (x1 - x3) * (y - y3)) / denominator;
double gamma = 1 - alpha - beta;
//# Check if the barycentric coordinates are within the range [0, 1].
if( (0 <= alpha && alpha <= 1) && (0 <= beta && beta <= 1) && (0 <= gamma && gamma <= 1) )
return 1;
else
return 0;
}
double Distance2D(double x1, double y1, double x2, double y2)
{
return sqrt( pow((x2 - x1),2) + pow((y2 - y1),2) );
}
double GetCircleLineIntersection(double cx, double cy, double r, double x1, double y1, double x2, double y2, double* ix1, double* iy1, double* ix2, double* iy2)
{
//'"""
//'Calculate the intersection points between a line defined by two points (x1, y1) and (x2, y2),
//'and a circle with center (cx, cy) and radius r.
//'"""
double dx = x2 - x1;
double dy = y2 - y1;
double a = pow(dx,2) + pow(dy,2);
double b = 2 * (dx * (x1 - cx) + dy * (y1 - cy));
double c = pow((x1 - cx),2) + pow((y1 - cy),2) - pow(r,2);
double discriminant = pow(b,2) - 4 * a * c;
if(discriminant < 0)
{
//'# No intersection points
return 0;
}
else if( discriminant == 0 )
{
//'# One intersection point
double t = -b / (2 * a);
ix1[0] = x1 + t * dx;
iy1[0] = y1 + t * dy;
return 1;
}
else
{
//'# Two intersection points
double t1 = (-b + sqrt(discriminant)) / (2 * a);
double t2 = (-b - sqrt(discriminant)) / (2 * a);
ix1[0] = x1 + t1 * dx;
iy1[0] = y1 + t1 * dy;
ix2[0] = x1 + t2 * dx;
iy2[0] = y1 + t2 * dy;
return 2;
}
}
int CAMERA_LENS = 0;
// 'Returns number of points in clipped triangle Or 0 if no clipping was done
int ClipTriangle(double* tri, double* uv, double* clipped_tri, double* clipped_uv)
{
int clip_count = 0;
double lp[3], ld[3], p1[3], p2[3], p3[3], intersect[3];
double AB_dist, AC_dist, dist;
//'vec(lp, 20, 30, 265)
//'vec(ld, 20 - lp[0], 20 - lp[1], 275 - lp[2])
int clip_dist = CAMERA_LENS-1;
p1[0] = -1;
p1[1] = -1;
p1[2] = clip_dist;
p2[0] = 1;
p2[1] = 1;
p2[2] = clip_dist;
p3[0] = 1;
p3[1] = -1;
p3[2] = clip_dist;
//'C3D_LinePlaneIntersection(lp, ld, p1, p2, p3, intersect)
double pt[9];
double pt_uv[6];
double nc[9];
double nc_uv[6];
int non_clip_count = 0;
int c_index = 0;
int pt_uv_index = 0;
int nc_uv_index = 0;
int uv_index = 0;
for(int i = 0; i <= 8; i+=3)
{
if( tri[i+2] >= clip_dist )
{
c_index = clip_count*3;
pt[c_index] = tri[i];
pt[c_index+1] = tri[i+1];
pt[c_index+2] = tri[i+2];
pt_uv_index = clip_count*2;
uv_index = i/3*2;
pt_uv[pt_uv_index] = uv[uv_index];
pt_uv[pt_uv_index+1] = uv[uv_index+1];
clip_count = clip_count + 1;
}
else
{
c_index = non_clip_count*3;
nc[c_index] = tri[i];
nc[c_index+1] = tri[i+1];
nc[c_index+2] = tri[i+2];
nc_uv_index = non_clip_count*2;
uv_index = i/3*2;
nc_uv[nc_uv_index] = uv[uv_index];
nc_uv[nc_uv_index+1] = uv[uv_index+1];
non_clip_count = non_clip_count + 1;
}
}
if(clip_count == 0 || clip_count == 3 )
{
return 0;
}
switch(clip_count)
{
case 1:
lp[0] = pt[0];
lp[1] = pt[1];
lp[2] = pt[2];
ld[0] = nc[0] - lp[0];
ld[1] = nc[1] - lp[1];
ld[2] = nc[2] - lp[2];
GetLinePlaneIntersection(&lp[0], &ld[0], &p1[0], &p2[0], &p3[0], &intersect[0]);
//'dim clipped_tri[3]
AB_dist = Distance3D(pt[0], pt[1], pt[2], nc[0], nc[1], nc[2]);
AC_dist = Distance3D(pt[0], pt[1], pt[2], nc[3], nc[4], nc[5]);
//'AB
clipped_tri[0] = intersect[0];
clipped_tri[1] = intersect[1];
clipped_tri[2] = intersect[2];
dist = Distance3D(pt[0], pt[1], pt[2], clipped_tri[0], clipped_tri[1], clipped_tri[2]);
clipped_uv[0] = Interpolate(0, AB_dist, dist, pt_uv[0], nc_uv[0]);
clipped_uv[1] = Interpolate(0, AB_dist, dist, pt_uv[1], nc_uv[1]);
//'B
clipped_tri[3] = nc[0];
clipped_tri[4] = nc[1];
clipped_tri[5] = nc[2];
clipped_uv[2] = nc_uv[0];
clipped_uv[3] = nc_uv[1];
//'print "TEST: ";clipped_uv[2];", ";clipped_uv[3]
//'C
clipped_tri[6] = nc[3];
clipped_tri[7] = nc[4];
clipped_tri[8] = nc[5];
clipped_uv[4] = nc_uv[2];
clipped_uv[5] = nc_uv[3];
//'print "TEST(C): (";clipped_tri[6];", ";clipped_tri[7];", ";clipped_tri[8];") (";clipped_uv[4];", ";clipped_uv[5];")"
//'AB
clipped_tri[9] = clipped_tri[0];
clipped_tri[10] = clipped_tri[1];
clipped_tri[11] = clipped_tri[2];
clipped_uv[6] = clipped_uv[0];
clipped_uv[7] = clipped_uv[1];
//'C
clipped_tri[12] = nc[3];
clipped_tri[13] = nc[4];
clipped_tri[14] = nc[5];
clipped_uv[8] = nc_uv[2];
clipped_uv[9] = nc_uv[3];
ld[0] = nc[3] - lp[0];
ld[1] = nc[4] - lp[1];
ld[2] = nc[5] - lp[2];
GetLinePlaneIntersection(&lp[0], &ld[0], &p1[0], &p2[0], &p3[0], &intersect[0]);
//'AC
clipped_tri[15] = intersect[0];
clipped_tri[16] = intersect[1];
clipped_tri[17] = intersect[2];
dist = Distance3D(pt[0], pt[1], pt[2], clipped_tri[15], clipped_tri[16], clipped_tri[17]);
clipped_uv[10] = Interpolate(0, AC_dist, dist, pt_uv[0], nc_uv[2]);
clipped_uv[11] = Interpolate(0, AC_dist, dist, pt_uv[1], nc_uv[3]);
return 6;
case 2:
//'A is the no clip
lp[0] = pt[0];
lp[1] = pt[1];
lp[2] = pt[2];
ld[0] = nc[0] - lp[0];
ld[1] = nc[1] - lp[1];
ld[2] = nc[2] - lp[2];
GetLinePlaneIntersection(&lp[0], &ld[0], &p1[0], &p2[0], &p3[0], &intersect[0]);
AB_dist = Distance3D(pt[0], pt[1], pt[2], nc[0], nc[1], nc[2]);
AC_dist = Distance3D(pt[3], pt[4], pt[5], nc[0], nc[1], nc[2]);
//'A
clipped_tri[0] = nc[0];
clipped_tri[1] = nc[1];
clipped_tri[2] = nc[2];
clipped_uv[0] = nc_uv[0];
clipped_uv[1] = nc_uv[1];
//'AB
clipped_tri[3] = intersect[0];
clipped_tri[4] = intersect[1];
clipped_tri[5] = intersect[2];
dist = Distance3D(nc[0], nc[1], nc[2], clipped_tri[3], clipped_tri[4], clipped_tri[5]);
clipped_uv[2] = Interpolate(0, AB_dist, dist, nc_uv[0], pt_uv[0]);
clipped_uv[3] = Interpolate(0, AB_dist, dist, nc_uv[1], pt_uv[1]);
//'AC
lp[0] = pt[3];
lp[1] = pt[4];
lp[2] = pt[5];
ld[0] = nc[0] - lp[0];
ld[1] = nc[1] - lp[1];
ld[2] = nc[2] - lp[2];
GetLinePlaneIntersection(&lp[0], &ld[0], &p1[0], &p2[0], &p3[0], &intersect[0]);
clipped_tri[6] = intersect[0];
clipped_tri[7] = intersect[1];
clipped_tri[8] = intersect[2];
dist = Distance3D(nc[0], nc[1], nc[2], clipped_tri[6], clipped_tri[7], clipped_tri[8]);
clipped_uv[4] = Interpolate(0, AC_dist, dist, nc_uv[0], pt_uv[2]);
clipped_uv[5] = Interpolate(0, AC_dist, dist, nc_uv[1], pt_uv[3]);
return 3;
}
return 0;
}
void projectionGeometry(int cam_dist, int f_vertex_count, double* vertex3D, double* uv, double graph_offset_x, double graph_offset_y, uint32_t color,
double* vertex_count, double* vertex2D, double* ind_count, double* index, double* clip_dist, double* min_x, double* min_y, double* max_x, double* max_y)
{
CAMERA_LENS = cam_dist;
if( f_vertex_count > 4 || f_vertex_count < 3)
return;
int tri_index = 0;
int uv_index = 0;
int clip = 0;
double clipped_tri[18], clipped_uv[12];
double distance = 0;
double cld = 0;
int vi = 0;
uint32_t r, g, b, a;
r = ( (color >> 16) & 255);
g = ( (color >> 8) & 255);
b = ( color & 255);
a = ( (color >> 24) & 255);
//cout << "color = " << color << ", " << r << ", " << g << ", " << b << ", " << a << endl;
int index_count = 0;
vertex_count[0] = 0;
int ni = 0;
switch(f_vertex_count)
{
case 3:
clip = ClipTriangle(&vertex3D[0], &uv[0], &clipped_tri[0], &clipped_uv[0]);
if(clip > 0)
{
tri_index = 0;
uv_index = 0;
for(int i = 0; i < clip; i++)
{
distance = CAMERA_LENS - clipped_tri[tri_index+2];
distance = (distance <= 0) ? 1 : distance;
cld = (CAMERA_LENS / distance);
ni = vi * 8;
vertex2D[ ni + 0 ] = (cld * clipped_tri[tri_index]) + graph_offset_x;
vertex2D[ ni + 1 ] = graph_offset_y - (cld * clipped_tri[tri_index+1]);
vertex2D[ ni + 2 ] = r;
vertex2D[ ni + 3 ] = g;
vertex2D[ ni + 4 ] = b;
vertex2D[ ni + 5 ] = a;
vertex2D[ ni + 6 ] = clipped_uv[uv_index]; //' uv_x + (uv_w * C3D_Mesh_TCoord[mesh, C3D_Mesh_Face_TCoord[mesh, face, i], 0]) 'u
vertex2D[ ni + 7 ] = clipped_uv[uv_index+1]; //'uv_y + (uv_h * C3D_Mesh_TCoord[mesh, C3D_Mesh_Face_TCoord[mesh, face, i], 1]) 'v
clip_dist[0] = min(distance, clip_dist[0]);
min_x[0] = min(vertex2D[ ni + 0], min_x[0]);
min_y[0] = min(vertex2D[ ni + 1], min_y[0]);
max_x[0] = max(vertex2D[ ni + 0], max_x[0]);
max_y[0] = max(vertex2D[ ni + 1], max_y[0]);
index[index_count] = vi;
index_count = index_count + 1;
vi = vi + 1;
tri_index = tri_index + 3;
uv_index = uv_index + 2;
}
}
else
{
tri_index = 0;
uv_index = 0;
for(int i = 0; i < 3; i++)
{
distance = CAMERA_LENS - vertex3D[tri_index+2];
distance = (distance<=0) ? 1 : distance;
cld = (CAMERA_LENS / distance);
ni = vi * 8;
vertex2D[ ni + 0 ] = (cld * vertex3D[tri_index]) + graph_offset_x;
vertex2D[ ni + 1 ] = graph_offset_y - (cld * vertex3D[tri_index+1]);
vertex2D[ ni + 2 ] = r;
vertex2D[ ni + 3 ] = g;
vertex2D[ ni + 4 ] = b;
vertex2D[ ni + 5 ] = a;
vertex2D[ ni + 6 ] = uv[uv_index]; //' uv_x + (uv_w * C3D_Mesh_TCoord[mesh, C3D_Mesh_Face_TCoord[mesh, face, i], 0]) 'u
vertex2D[ ni + 7 ] = uv[uv_index+1]; //'uv_y + (uv_h * C3D_Mesh_TCoord[mesh, C3D_Mesh_Face_TCoord[mesh, face, i], 1]) 'v
clip_dist[0] = min(distance, clip_dist[0]);
min_x[0] = min(vertex2D[ ni + 0], min_x[0]);
min_y[0] = min(vertex2D[ ni + 1], min_y[0]);
max_x[0] = max(vertex2D[ ni + 0], max_x[0]);
max_y[0] = max(vertex2D[ ni + 1], max_y[0]);
vi = vi + 1;
tri_index = tri_index + 3;
uv_index = uv_index + 2;
}
index[index_count] = 0;
index[index_count+1] = 1;
index[index_count+2] = 2;
index_count = index_count + 3;
}
break;
case 4:
clip = ClipTriangle(&vertex3D[0], &uv[0], &clipped_tri[0], &clipped_uv[0]);
if(clip > 0)
{
tri_index = 0;
uv_index = 0;
for(int i = 0; i < clip; i++)
{
distance = CAMERA_LENS - clipped_tri[tri_index+2];
distance = (distance<=0) ? 1 : distance;
cld = (CAMERA_LENS / distance);
ni = vi * 8;
vertex2D[ ni + 0 ] = (cld * clipped_tri[tri_index]) + graph_offset_x;
vertex2D[ ni + 1 ] = graph_offset_y - (cld * clipped_tri[tri_index+1]);
vertex2D[ ni + 2 ] = r;
vertex2D[ ni + 3 ] = g;
vertex2D[ ni + 4 ] = b;
vertex2D[ ni + 5 ] = a;
vertex2D[ ni + 6 ] = clipped_uv[uv_index]; //' uv_x + (uv_w * C3D_Mesh_TCoord[mesh, C3D_Mesh_Face_TCoord[mesh, face, i], 0]) 'u
vertex2D[ ni + 7 ] = clipped_uv[uv_index+1]; //'uv_y + (uv_h * C3D_Mesh_TCoord[mesh, C3D_Mesh_Face_TCoord[mesh, face, i], 1]) 'v
clip_dist[0] = min(distance, clip_dist[0]);
min_x[0] = min(vertex2D[ ni + 0], min_x[0]);
min_y[0] = min(vertex2D[ ni + 1], min_y[0]);
max_x[0] = max(vertex2D[ ni + 0], max_x[0]);
max_y[0] = max(vertex2D[ ni + 1], max_y[0]);
index[index_count] = vi;
index_count = index_count + 1;
vi = vi + 1;
tri_index = tri_index + 3;
uv_index = uv_index + 2;
}
}
else
{
//cout << "DBG: " << vertex3D[0] << ", " << vertex3D[1] << endl;
tri_index = 0;
uv_index = 0;
for(int i = 0; i < 3; i++)
{
distance = CAMERA_LENS - vertex3D[tri_index+2];
distance = (distance<=0) ? 1 : distance;
cld = (CAMERA_LENS / distance);
ni = vi * 8;
vertex2D[ ni + 0 ] = (cld * vertex3D[tri_index]) + graph_offset_x;
vertex2D[ ni + 1 ] = graph_offset_y - (cld * vertex3D[tri_index+1]);
vertex2D[ ni + 2 ] = r;
vertex2D[ ni + 3 ] = g;
vertex2D[ ni + 4 ] = b;
vertex2D[ ni + 5 ] = a;
vertex2D[ ni + 6 ] = uv[uv_index]; //' uv_x + (uv_w * C3D_Mesh_TCoord[mesh, C3D_Mesh_Face_TCoord[mesh, face, i], 0]) 'u
vertex2D[ ni + 7 ] = uv[uv_index+1]; //'uv_y + (uv_h * C3D_Mesh_TCoord[mesh, C3D_Mesh_Face_TCoord[mesh, face, i], 1]) 'v
clip_dist[0] = min(distance, clip_dist[0]);
min_x[0] = min(vertex2D[ ni + 0], min_x[0]);
min_y[0] = min(vertex2D[ ni + 1], min_y[0]);
max_x[0] = max(vertex2D[ ni + 0], max_x[0]);
max_y[0] = max(vertex2D[ ni + 1], max_y[0]);
vi = vi + 1;
tri_index = tri_index + 3;
uv_index = uv_index + 2;
}
index[index_count] = 0;
index[index_count+1] = 1;
index[index_count+2] = 2;
index_count = index_count + 3;
}
vertex3D[3] = vertex3D[0];
vertex3D[4] = vertex3D[1];
vertex3D[5] = vertex3D[2];
uv[2] = uv[0];
uv[3] = uv[1];
clip = ClipTriangle(&vertex3D[3], &uv[2], &clipped_tri[0], &clipped_uv[0]);
if(clip > 0)
{
tri_index = 0;
uv_index = 0;
for(int i = 0; i < clip; i++)
{
distance = CAMERA_LENS - clipped_tri[tri_index+2];
distance = (distance<=0) ? 1 : distance;
cld = (CAMERA_LENS / distance);
ni = vi * 8;
vertex2D[ ni + 0 ] = (cld * clipped_tri[tri_index]) + graph_offset_x;
vertex2D[ ni + 1 ] = graph_offset_y - (cld * clipped_tri[tri_index+1]);
vertex2D[ ni + 2 ] = r;
vertex2D[ ni + 3 ] = g;
vertex2D[ ni + 4 ] = b;
vertex2D[ ni + 5 ] = a;
vertex2D[ ni + 6 ] = clipped_uv[uv_index]; //' uv_x + (uv_w * C3D_Mesh_TCoord[mesh, C3D_Mesh_Face_TCoord[mesh, face, i], 0]) 'u
vertex2D[ ni + 7 ] = clipped_uv[uv_index+1]; //'uv_y + (uv_h * C3D_Mesh_TCoord[mesh, C3D_Mesh_Face_TCoord[mesh, face, i], 1]) 'v
clip_dist[0] = min(distance, clip_dist[0]);
min_x[0] = min(vertex2D[ ni + 0], min_x[0]);
min_y[0] = min(vertex2D[ ni + 1], min_y[0]);
max_x[0] = max(vertex2D[ ni + 0], max_x[0]);
max_y[0] = max(vertex2D[ ni + 1], max_y[0]);
index[index_count] = vi;
index_count = index_count + 1;
vi = vi + 1;
tri_index = tri_index + 3;
uv_index = uv_index + 2;
}
}
else
{
tri_index = 3;
uv_index = 2;
for(int i = 0; i < 3; i++)
{
distance = CAMERA_LENS - vertex3D[tri_index+2];
distance = (distance<=0) ? 1 : distance;
cld = (CAMERA_LENS / distance);
ni = vi * 8;
vertex2D[ ni + 0 ] = (cld * vertex3D[tri_index]) + graph_offset_x;
vertex2D[ ni + 1 ] = graph_offset_y - (cld * vertex3D[tri_index+1]);
vertex2D[ ni + 2 ] = r;
vertex2D[ ni + 3 ] = g;
vertex2D[ ni + 4 ] = b;
vertex2D[ ni + 5 ] = a;
vertex2D[ ni + 6 ] = uv[uv_index]; //' uv_x + (uv_w * C3D_Mesh_TCoord[mesh, C3D_Mesh_Face_TCoord[mesh, face, i], 0]) 'u
vertex2D[ ni + 7 ] = uv[uv_index+1]; //'uv_y + (uv_h * C3D_Mesh_TCoord[mesh, C3D_Mesh_Face_TCoord[mesh, face, i], 1]) 'v
clip_dist[0] = min(distance, clip_dist[0]);
min_x[0] = min(vertex2D[ ni + 0], min_x[0]);
min_y[0] = min(vertex2D[ ni + 1], min_y[0]);
max_x[0] = max(vertex2D[ ni + 0], max_x[0]);
max_y[0] = max(vertex2D[ ni + 1], max_y[0]);
index[index_count] = vi; //'They will already be in the right order here
index_count = index_count + 1;
vi = vi + 1;
tri_index = tri_index + 3;
uv_index = uv_index + 2;
}
}
break;
}
vertex_count[0] = (double)vi;
ind_count[0] = (double)index_count;
//cout << "DEBUG: " << vertex_count[0] << ", " << ind_count[0] << endl;
}
void rc_GetProjectionGeometry(int cam_dist, uint32_t mA, int f_vertex_count, double* columns, double* uv, double graph_offset_x, double graph_offset_y, uint32_t color,
double* vertex_count, double* vertex2D, double* ind_count, double* index, double* clip_dist, double* min_x, double* min_y, double* max_x, double* max_y)
{
double vertex3D[18]; // number of vertices * 3 -> ie. (x,y,z) for each vertex
int v_index = 0;
for(int i = 0; i < f_vertex_count; i++)
{
vertex3D[v_index] = MatrixValue(mA, 0, columns[i]);
vertex3D[v_index+1] = MatrixValue(mA, 1, columns[i]);
vertex3D[v_index+2] = MatrixValue(mA, 2, columns[i]);
v_index += 3;
}
projectionGeometry(cam_dist, f_vertex_count, &vertex3D[0], uv, graph_offset_x, graph_offset_y, color,
vertex_count, vertex2D, ind_count, index, clip_dist, min_x, min_y, max_x, max_y);
}
// --------------------------------------------------------------------------
#endif // RC_GEOMETRY_H_INCLUDED

1189
rcbasic_runtime/rc_matrix.h Normal file

File diff suppressed because it is too large Load Diff

6502
rcbasic_runtime/rc_media.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,23 @@
#ifndef RC_OS_DEFINES_H_INCLUDED
#define RC_OS_DEFINES_H_INCLUDED
#define RC_LINUX
//#define RC_WEB
//#define RC_WINDOWS
//#define RC_ANDROID
//#define RC_MAC
//#define RC_IOS
#ifdef RC_WEB
#define RC_LINUX
#endif
#ifdef RC_ANDROID
#define RC_MOBILE
#endif
#ifdef RC_IOS
#define RC_MOBILE
#endif
#endif // RC_OS_DEFINES_H_INCLUDED

View File

@@ -0,0 +1,555 @@
#ifndef RC_PROCESS_H_INCLUDED
#define RC_PROCESS_H_INCLUDED
#include <iostream>
#ifdef RC_ANDROID
#include "SDL.h"
#else
#include <SDL2/SDL.h>
#endif
#include <queue>
#include "rc_matrix.h"
#include "rc_defines.h"
#define MAX_SUBPROCESS 3
#define RC_THREAD_ONERROR_CONTINUE 0
#define RC_THREAD_ONERROR_STOP 1
SDL_Thread* rc_subprocess_thread[3];
struct rc_threadData
{
uint32_t sub_process_num;
};
rc_threadData rc_subprocess_param[3];
struct rc_subprocess_mutex_container
{
SDL_mutex* queue_mutex;
SDL_mutex* current_op_mutex;
SDL_mutex* error_mode_mutex;
bool queue_mutex_locked;
bool current_op_mutex_locked;
bool error_mode_mutex_locked;
};
rc_subprocess_mutex_container rc_subprocess_mutex[3];
struct rc_process_op
{
int fn;
double n[10];
// I will add a string arg whenever a string op gets implemented
};
struct rc_process_op_list
{
queue<rc_process_op> op;
};
rc_process_op_list rc_subprocess_queue[3];
uint32_t rc_subprocess_error_mode[3];
int rc_subprocess_error[3];
int rc_active_matrix_process = -1;
void rc_initSubprocessSystem()
{
for(int i = 0; i < MAX_SUBPROCESS; i++)
{
rc_subprocess_thread[i] = NULL;
rc_subprocess_mutex[i].current_op_mutex = SDL_CreateMutex();
rc_subprocess_mutex[i].error_mode_mutex = SDL_CreateMutex();
rc_subprocess_mutex[i].queue_mutex = SDL_CreateMutex();
rc_subprocess_mutex[i].current_op_mutex_locked = false;
rc_subprocess_mutex[i].error_mode_mutex_locked = false;
rc_subprocess_mutex[i].queue_mutex_locked = false;
}
}
#define RC_QUEUE_MUTEX 1
#define RC_ERROR_MODE_MUTEX 2
#define RC_CURRENT_OP_MUTEX 3
void rc_lockMutex(int process_num, uint32_t m)
{
switch(m)
{
case RC_QUEUE_MUTEX:
if(!rc_subprocess_mutex[process_num].queue_mutex_locked)
SDL_LockMutex(rc_subprocess_mutex[process_num].queue_mutex);
rc_subprocess_mutex[process_num].queue_mutex_locked = true;
break;
case RC_ERROR_MODE_MUTEX:
if(!rc_subprocess_mutex[process_num].error_mode_mutex_locked)
SDL_LockMutex(rc_subprocess_mutex[process_num].error_mode_mutex);
rc_subprocess_mutex[process_num].error_mode_mutex_locked = true;
break;
case RC_CURRENT_OP_MUTEX:
if(!rc_subprocess_mutex[process_num].current_op_mutex_locked)
SDL_LockMutex(rc_subprocess_mutex[process_num].current_op_mutex);
rc_subprocess_mutex[process_num].current_op_mutex_locked = true;
break;
}
}
void rc_unlockMutex(int process_num, uint32_t m)
{
switch(m)
{
case RC_QUEUE_MUTEX:
rc_subprocess_mutex[process_num].queue_mutex_locked = false;
SDL_UnlockMutex(rc_subprocess_mutex[process_num].queue_mutex);
break;
case RC_ERROR_MODE_MUTEX:
rc_subprocess_mutex[process_num].error_mode_mutex_locked = false;
SDL_UnlockMutex(rc_subprocess_mutex[process_num].error_mode_mutex);
break;
case RC_CURRENT_OP_MUTEX:
rc_subprocess_mutex[process_num].current_op_mutex_locked = false;
SDL_UnlockMutex(rc_subprocess_mutex[process_num].current_op_mutex);
break;
}
}
int rc_subprocess_fn( void* data)
{
rc_threadData* param = (rc_threadData*) data;
int process_num = param->sub_process_num;
//std::cout << "thread start " << process_num << std::endl;
rc_process_op op;
bool p_loop = true;
while(p_loop)
{
op.fn = -1;
rc_lockMutex(process_num, RC_QUEUE_MUTEX);
if(rc_subprocess_queue[process_num].op.size() > 0)
{
op = rc_subprocess_queue[process_num].op.front();
rc_unlockMutex(process_num, RC_QUEUE_MUTEX);
}
else
rc_unlockMutex(process_num, RC_QUEUE_MUTEX);
rc_lockMutex(process_num, RC_CURRENT_OP_MUTEX);
rc_lockMutex(process_num, RC_ERROR_MODE_MUTEX);
switch(op.fn)
{
case 0:
p_loop = false; //std::cout << "THREAD_END_OP " << std::endl;
break;
case FN_DimMatrix: //Sub Procedure
DimMatrix(op.n[0], op.n[1], op.n[2], op.n[3]);
break;
case FN_AddMatrix: //Number Function
rc_subprocess_error[process_num] = (AddMatrix(op.n[0], op.n[1], op.n[2])) ? 0 : op.fn;
break;
case FN_AugmentMatrix: //Number Function
rc_subprocess_error[process_num] = (AugmentMatrix(op.n[0], op.n[1], op.n[2])) ? 0 : op.fn;
break;
case FN_CopyMatrix: //Sub Procedure
CopyMatrix(op.n[0], op.n[1]);
break;
case FN_InsertMatrixColumns: //Number Function
rc_subprocess_error[process_num] = (InsertMatrixColumn(op.n[0], op.n[1], op.n[2])) ? 0 : op.fn;
break;
case FN_InsertMatrixRows: //Number Function
rc_subprocess_error[process_num] = (InsertMatrixRow(op.n[0], op.n[1], op.n[2])) ? 0 : op.fn;
break;
case FN_MultiplyMatrix: //Number Function
rc_subprocess_error[process_num] = (MultiplyMatrix(op.n[0], op.n[1], op.n[2])) ? 0 : op.fn;
break;
case FN_CubeMatrix: //Number Function
rc_subprocess_error[process_num] = (CubeMatrix(op.n[0], op.n[1], process_num)) ? 0 : op.fn;
break;
case FN_DeleteMatrixColumns: //Number Function
rc_subprocess_error[process_num] = (DeleteMatrixColumns(op.n[0], op.n[1], op.n[2], process_num)) ? 0 : op.fn;
break;
case FN_DeleteMatrixRows: //Number Function
rc_subprocess_error[process_num] = (DeleteMatrixRows(op.n[0], op.n[1], op.n[2])) ? 0 : op.fn;
break;
case FN_ClearMatrix: //Sub Procedure
ClearMatrix(op.n[0]);
break;
case FN_ClearMatrixColumns: //Number Function
rc_subprocess_error[process_num] = (ClearMatrixColumns(op.n[0], op.n[1], op.n[2])) ? 0 : op.fn;
break;
case FN_ClearMatrixRows: //Number Function
rc_subprocess_error[process_num] = (ClearMatrixRows(op.n[0], op.n[1], op.n[2])) ? 0 : op.fn;
break;
case FN_FillMatrix: //Sub Procedure
FillMatrix(op.n[0], op.n[1]);
break;
case FN_FillMatrixColumns: //Number Function
rc_subprocess_error[process_num] = (FillMatrixColumns(op.n[0], op.n[1], op.n[2], op.n[3])) ? 0 : op.fn;
break;
case FN_FillMatrixRows: //Number Function
rc_subprocess_error[process_num] = (FillMatrixRows(op.n[0], op.n[1], op.n[2], op.n[3])) ? 0 : op.fn;
break;
case FN_CopyMatrixColumns: //Number Function
rc_subprocess_error[process_num] = (GetMatrixColumns(op.n[0], op.n[1], op.n[2], op.n[3])) ? 0 : op.fn; // 1 needs to be replaced with error code
break;
case FN_CopyMatrixRows: //Number Function
rc_subprocess_error[process_num] = (GetMatrixRows(op.n[0], op.n[1], op.n[2], op.n[3])) ? 0 : op.fn; // 1 needs to be replaced with error code
break;
case FN_IdentityMatrix: //Sub Procedure
IdentityMatrix(op.n[0], op.n[1]);
break;
case FN_SolveMatrix: //Number Function
rc_subprocess_error[process_num] = (SolveMatrix(op.n[0], op.n[1], op.n[2])) ? 0 : op.fn; // 1 needs to be replaced with error code
break;
case FN_IsEqualMatrix: //Number Function
rc_subprocess_error[process_num] = (IsEqualMatrix(op.n[0], op.n[1], op.n[2])) ? 0 : op.fn; // 1 needs to be replaced with error code
break;
case FN_AdjointMatrix: //Number Function
rc_subprocess_error[process_num] = (AdjointMatrix(op.n[0], op.n[1])) ? 0 : op.fn; // 1 needs to be replaced with error code
break;
case FN_InvertMatrix: //Number Function
rc_subprocess_error[process_num] = (InvertMatrix(op.n[0], op.n[1])) ? 0 : op.fn; // 1 needs to be replaced with error code
break;
case FN_RandomizeMatrix: //Sub Procedure
RandomizeMatrix(op.n[0], op.n[1], op.n[2]);
break;
case FN_SetMatrixValue: //Sub Procedure
SetMatrixValue(op.n[0], op.n[1], op.n[2], op.n[3]);
break;
case FN_ScalarMatrix: //Sub Procedure
ScalarMatrix(op.n[0], op.n[1], op.n[2]);
break;
case FN_ScalarMatrixColumns: //Number Function
rc_subprocess_error[process_num] = (ScalarMatrixColumns(op.n[0], op.n[1], op.n[2], op.n[3], op.n[4])) ? 0 : op.fn; // 1 needs to be replaced with error code
break;
case FN_ScalarMatrixRows: //Number Function
rc_subprocess_error[process_num] = (ScalarMatrixRows(op.n[0], op.n[1], op.n[2], op.n[3], op.n[4])) ? 0 : op.fn; // 1 needs to be replaced with error code
break;
case FN_SquareMatrix: //Number Function
rc_subprocess_error[process_num] = (SquareMatrix(op.n[0], op.n[1])) ? 0 : op.fn; // 1 needs to be replaced with error code
break;
case FN_SubMatrix: //Sub Procedure
SubMatrix(op.n[0], op.n[1], op.n[2]);
break;
case FN_SubtractMatrix: //Number Function
rc_subprocess_error[process_num] = (SubtractMatrix(op.n[0], op.n[1], op.n[2])) ? 0 : op.fn; // 1 needs to be replaced with error code
break;
case FN_SwapMatrix: //Sub Procedure
SwapMatrix(op.n[0], op.n[1], process_num); // 1 needs to be replaced with error code
break;
case FN_SwapMatrixColumn: //Number Function
rc_subprocess_error[process_num] = (SwapMatrixColumn(op.n[0], op.n[1], op.n[2])) ? 0 : op.fn; // 1 needs to be replaced with error code
break;
case FN_SwapMatrixRow: //Number Function
rc_subprocess_error[process_num] = (SwapMatrixRow(op.n[0], op.n[1], op.n[2])) ? 0 : op.fn; // 1 needs to be replaced with error code
break;
case FN_TransposeMatrix: //Number Function
rc_subprocess_error[process_num] = (TransposeMatrix(op.n[0], op.n[1])) ? 0 : op.fn; // 1 needs to be replaced with error code
break;
case FN_UnAugmentMatrix: //Number Function
rc_subprocess_error[process_num] = (UnAugmentMatrix(op.n[0], op.n[1], op.n[2])) ? 0 : op.fn; // 1 needs to be replaced with error code
break;
case FN_ZeroMatrix: //Sub Procedure
ZeroMatrix(op.n[0]);
break;
case FN_ProcessSleep: //Sub Procedure
SDL_Delay(op.n[0]);
break;
}
if(op.fn >= 0)
{
rc_lockMutex(process_num, RC_QUEUE_MUTEX);
if(rc_subprocess_queue[process_num].op.size()>0)
rc_subprocess_queue[process_num].op.pop();
rc_unlockMutex(process_num, RC_QUEUE_MUTEX);
}
if(rc_subprocess_error[process_num] != 0)
{
switch(rc_subprocess_error_mode[process_num])
{
case RC_THREAD_ONERROR_CONTINUE:
rc_unlockMutex(process_num, RC_ERROR_MODE_MUTEX);
break;
case RC_THREAD_ONERROR_STOP:
rc_unlockMutex(process_num, RC_ERROR_MODE_MUTEX);
bool error_status = true;
while(error_status)
{
rc_lockMutex(process_num, RC_ERROR_MODE_MUTEX);
error_status = (rc_subprocess_error[process_num] != 0);
rc_unlockMutex(process_num, RC_ERROR_MODE_MUTEX);
//std::cout << "THREAD_ERROR " << error_status << std::endl;
SDL_Delay(5);
}
break;
}
}
else
rc_unlockMutex(process_num, RC_ERROR_MODE_MUTEX);
rc_unlockMutex(process_num, RC_CURRENT_OP_MUTEX);
}
//std::cout << "thread end " << process_num << std::endl;
return 0;
}
bool rc_setMatrixProcess(int p_num)
{
if(p_num >= 0 && p_num < MAX_SUBPROCESS)
rc_active_matrix_process = p_num;
else
rc_active_matrix_process = -1;
if(rc_active_matrix_process >= 0)
return (rc_subprocess_thread[rc_active_matrix_process] != NULL);
return true; //main process is set
}
bool rc_processOpen(int p_num)
{
if(p_num < 0 || p_num >= MAX_SUBPROCESS)
return false;
if(rc_subprocess_thread[p_num])
return false;
for(int i = 0; i < rc_subprocess_queue[p_num].op.size(); i++)
rc_subprocess_queue[p_num].op.pop();
rc_subprocess_thread[p_num] = SDL_CreateThread(rc_subprocess_fn, "rc_subprocess", &rc_subprocess_param[0]);
return (rc_subprocess_thread[p_num] != NULL);
}
void rc_setProcessErrorMode(int p_num, uint32_t error_mode)
{
if(p_num < 0 || p_num >= MAX_SUBPROCESS)
return;
if(error_mode < 0 || error_mode > 1)
error_mode = 0;
rc_lockMutex(p_num, RC_ERROR_MODE_MUTEX);
rc_subprocess_error_mode[p_num] = error_mode;
rc_unlockMutex(p_num, RC_ERROR_MODE_MUTEX);
}
int ProcessError(int p_num)
{
if(p_num < 0 || p_num >= MAX_SUBPROCESS)
return 0;
rc_lockMutex(p_num, RC_ERROR_MODE_MUTEX);
int error = rc_subprocess_error[p_num];
rc_unlockMutex(p_num, RC_ERROR_MODE_MUTEX);
return error;
}
void ProcessWait(int p_num)
{
if(p_num < 0 || p_num >= MAX_SUBPROCESS)
return;
if(!rc_subprocess_thread[p_num])
return;
//std::cout << "q_LOCK " << std::endl;
rc_lockMutex(p_num, RC_QUEUE_MUTEX);
uint32_t q_size = rc_subprocess_queue[p_num].op.size();
rc_unlockMutex(p_num, RC_QUEUE_MUTEX);
//std::cout << "q_size = " << q_size << std::endl;
while(q_size > 0)
{
rc_lockMutex(p_num, RC_QUEUE_MUTEX);
q_size = rc_subprocess_queue[p_num].op.size();
rc_unlockMutex(p_num, RC_QUEUE_MUTEX);
}
}
void ProcessWaitAll()
{
for(uint32_t i = 0; i < MAX_SUBPROCESS; i++)
ProcessWait(i);
}
void ProcessContinue(int p_num)
{
if(p_num < 0 || p_num >= MAX_SUBPROCESS)
return;
rc_lockMutex(p_num, RC_ERROR_MODE_MUTEX);
rc_subprocess_error[p_num] = 0;
rc_unlockMutex(p_num, RC_ERROR_MODE_MUTEX);
rc_unlockMutex(p_num, RC_CURRENT_OP_MUTEX);
}
void ProcessContinueAll()
{
for(uint32_t i = 0; i < MAX_SUBPROCESS; i++)
ProcessContinue(i);
}
void ProcessStop(int p_num)
{
if(p_num < 0 || p_num >= MAX_SUBPROCESS)
return;
rc_lockMutex(p_num, RC_CURRENT_OP_MUTEX);
}
void ProcessStopAll()
{
for(uint32_t i = 0; i < MAX_SUBPROCESS; i++)
ProcessStop(i);
}
void ProcessClear(int p_num)
{
if(p_num < 0 || p_num >= MAX_SUBPROCESS)
return;
rc_lockMutex(p_num, RC_QUEUE_MUTEX);
uint32_t q_size = rc_subprocess_queue[p_num].op.size();
for(uint32_t i = 0; i < q_size; i++)
rc_subprocess_queue[p_num].op.pop();
rc_unlockMutex(p_num, RC_QUEUE_MUTEX);
rc_lockMutex(p_num, RC_ERROR_MODE_MUTEX);
rc_subprocess_error[p_num] = 0;
rc_unlockMutex(p_num, RC_ERROR_MODE_MUTEX);
}
bool ProcessClose(int p_num)
{
if(p_num < 0 || p_num >= MAX_SUBPROCESS)
return false;
if(!rc_subprocess_thread[p_num])
return false;
rc_process_op op;
op.fn = 0;
ProcessClear(p_num);
rc_lockMutex(p_num, RC_QUEUE_MUTEX);
rc_subprocess_queue[p_num].op.push(op);
rc_unlockMutex(p_num, RC_QUEUE_MUTEX);
SDL_WaitThread(rc_subprocess_thread[p_num], NULL);
rc_subprocess_thread[p_num] = NULL;
if(rc_active_matrix_process == p_num)
rc_active_matrix_process = -1;
return true;
}
uint32_t ProcessErrorMode(int p_num)
{
if(p_num < 0 || p_num >= MAX_SUBPROCESS)
return 0;
rc_lockMutex(p_num, RC_ERROR_MODE_MUTEX);
uint32_t error_mode = rc_subprocess_error_mode[p_num];
rc_unlockMutex(p_num, RC_ERROR_MODE_MUTEX);
return error_mode;
}
void ProcessSleep(int p_num, uint32_t msec)
{
if(p_num < 0 || p_num >= MAX_SUBPROCESS)
return;
rc_process_op op;
op.fn = FN_ProcessSleep;
op.n[0] = msec;
rc_lockMutex(p_num, RC_QUEUE_MUTEX);
rc_subprocess_queue[p_num].op.push(op);
rc_unlockMutex(p_num, RC_QUEUE_MUTEX);
}
bool ProcessExists(int p_num)
{
if(p_num < 0 || p_num >= MAX_SUBPROCESS)
return false;
return (rc_subprocess_thread[p_num] != NULL);
}
uint32_t ProcessQueueSize(int p_num)
{
rc_lockMutex(p_num, RC_QUEUE_MUTEX);
int q_size = rc_subprocess_queue[p_num].op.size();
rc_unlockMutex(p_num, RC_QUEUE_MUTEX);
return q_size;
}
void ProcessQueueMatrixOp(uint32_t fn, double n0 = 0, double n1 = 0, double n2 = 0, double n3 = 0, double n4 = 0, double n5 = 0,
double n6 = 0, double n7 = 0, double n8 = 0, double n9 = 0)
{
rc_process_op op;
op.fn = fn;
op.n[0] = n0;
op.n[1] = n1;
op.n[2] = n2;
op.n[3] = n3;
op.n[4] = n4;
op.n[5] = n5;
op.n[6] = n6;
op.n[7] = n7;
op.n[8] = n8;
op.n[9] = n9;
rc_lockMutex(rc_active_matrix_process, RC_QUEUE_MUTEX);
rc_subprocess_queue[rc_active_matrix_process].op.push(op);
rc_unlockMutex(rc_active_matrix_process, RC_QUEUE_MUTEX);
}
void rc_cleanSubprocessSystem()
{
for(int i = 0; i < MAX_SUBPROCESS; i++)
{
ProcessClose(i);
SDL_DestroyMutex(rc_subprocess_mutex[i].current_op_mutex);
SDL_DestroyMutex(rc_subprocess_mutex[i].error_mode_mutex);
SDL_DestroyMutex(rc_subprocess_mutex[i].queue_mutex);
}
}
int NumCPUs()
{
return SDL_GetCPUCount();
}
#endif // RC_PROCESS_H_INCLUDED

1556
rcbasic_runtime/rc_stdlib.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
MAINICON ICON "rcbasic.ico"

View File

@@ -0,0 +1,820 @@
/**
* TheoraPlay; multithreaded Ogg Theora/Ogg Vorbis decoding.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
// I wrote this with a lot of peeking at the Theora example code in
// libtheora-1.1.1/examples/player_example.c, but this is all my own
// code.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "SDL2/SDL.h"
#ifdef _WIN32
#include <windows.h>
#define THEORAPLAY_THREAD_T HANDLE
#define THEORAPLAY_MUTEX_T HANDLE
#define sleepms(x) Sleep(x)
#else
#include <pthread.h>
#include <unistd.h>
#define sleepms(x) usleep((x) * 1000)
#define THEORAPLAY_THREAD_T pthread_t
#define THEORAPLAY_MUTEX_T pthread_mutex_t
#endif
#include "theoraplay.h"
#include "theora/theoradec.h"
#include "vorbis/codec.h"
#define THEORAPLAY_INTERNAL 1
typedef THEORAPLAY_VideoFrame VideoFrame;
typedef THEORAPLAY_AudioPacket AudioPacket;
// !!! FIXME: these all count on the pixel format being TH_PF_420 for now.
typedef unsigned char *(*ConvertVideoFrameFn)(const th_info *tinfo,
const th_ycbcr_buffer ycbcr);
static unsigned char *ConvertVideoFrame420ToYUVPlanar(
const th_info *tinfo, const th_ycbcr_buffer ycbcr,
const int p0, const int p1, const int p2)
{
int i;
const int w = tinfo->pic_width;
const int h = tinfo->pic_height;
const int yoff = (tinfo->pic_x & ~1) + ycbcr[0].stride * (tinfo->pic_y & ~1);
const int uvoff = (tinfo->pic_x / 2) + (ycbcr[1].stride) * (tinfo->pic_y / 2);
unsigned char *yuv = (unsigned char *) malloc(w * h * 2);
if (yuv)
{
unsigned char *dst = yuv;
for (i = 0; i < h; i++, dst += w)
memcpy(dst, ycbcr[p0].data + yoff + ycbcr[p0].stride * i, w);
for (i = 0; i < (h / 2); i++, dst += w/2)
memcpy(dst, ycbcr[p1].data + uvoff + ycbcr[p1].stride * i, w / 2);
for (i = 0; i < (h / 2); i++, dst += w/2)
memcpy(dst, ycbcr[p2].data + uvoff + ycbcr[p2].stride * i, w / 2);
} // if
return yuv;
} // ConvertVideoFrame420ToYUVPlanar
static unsigned char *ConvertVideoFrame420ToYV12(const th_info *tinfo,
const th_ycbcr_buffer ycbcr)
{
return ConvertVideoFrame420ToYUVPlanar(tinfo, ycbcr, 0, 2, 1);
} // ConvertVideoFrame420ToYV12
static unsigned char *ConvertVideoFrame420ToIYUV(const th_info *tinfo,
const th_ycbcr_buffer ycbcr)
{
return ConvertVideoFrame420ToYUVPlanar(tinfo, ycbcr, 0, 1, 2);
} // ConvertVideoFrame420ToIYUV
// RGB
#define THEORAPLAY_CVT_FNNAME_420 ConvertVideoFrame420ToRGB
#define THEORAPLAY_CVT_RGB_ALPHA 0
#include "theoraplay_cvtrgb.h"
#undef THEORAPLAY_CVT_RGB_ALPHA
#undef THEORAPLAY_CVT_FNNAME_420
// RGBA
#define THEORAPLAY_CVT_FNNAME_420 ConvertVideoFrame420ToRGBA
#define THEORAPLAY_CVT_RGB_ALPHA 1
#include "theoraplay_cvtrgb.h"
#undef THEORAPLAY_CVT_RGB_ALPHA
#undef THEORAPLAY_CVT_FNNAME_420
typedef struct TheoraDecoder
{
// Thread wrangling...
int thread_created;
THEORAPLAY_MUTEX_T lock;
volatile int halt;
int thread_done;
THEORAPLAY_THREAD_T worker;
// API state...
THEORAPLAY_Io *io;
unsigned int maxframes; // Max video frames to buffer.
volatile unsigned int prepped;
volatile unsigned int videocount; // currently buffered frames.
volatile unsigned int audioms; // currently buffered audio samples.
volatile int hasvideo;
volatile int hasaudio;
volatile int decode_error;
THEORAPLAY_VideoFormat vidfmt;
ConvertVideoFrameFn vidcvt;
VideoFrame *videolist;
VideoFrame *videolisttail;
AudioPacket *audiolist;
AudioPacket *audiolisttail;
} TheoraDecoder;
#ifdef _WIN32
static inline int Thread_Create(TheoraDecoder *ctx, void *(*routine) (void*))
{
ctx->worker = CreateThread(
NULL,
0,
(LPTHREAD_START_ROUTINE) routine,
(LPVOID) ctx,
0,
NULL
);
return (ctx->worker == NULL);
}
static inline void Thread_Join(THEORAPLAY_THREAD_T thread)
{
WaitForSingleObject(thread, INFINITE);
CloseHandle(thread);
}
static inline int Mutex_Create(TheoraDecoder *ctx)
{
ctx->lock = CreateMutex(NULL, FALSE, NULL);
return (ctx->lock == NULL);
}
static inline void Mutex_Destroy(THEORAPLAY_MUTEX_T mutex)
{
CloseHandle(mutex);
}
static inline void Mutex_Lock(THEORAPLAY_MUTEX_T mutex)
{
WaitForSingleObject(mutex, INFINITE);
}
static inline void Mutex_Unlock(THEORAPLAY_MUTEX_T mutex)
{
ReleaseMutex(mutex);
}
#else
static inline int Thread_Create(TheoraDecoder *ctx, void *(*routine) (void*))
{
return pthread_create(&ctx->worker, NULL, routine, ctx);
}
static inline void Thread_Join(THEORAPLAY_THREAD_T thread)
{
pthread_join(thread, NULL);
}
static inline int Mutex_Create(TheoraDecoder *ctx)
{
return pthread_mutex_init(&ctx->lock, NULL);
}
static inline void Mutex_Destroy(THEORAPLAY_MUTEX_T mutex)
{
pthread_mutex_destroy(&mutex);
}
static inline void Mutex_Lock(THEORAPLAY_MUTEX_T mutex)
{
pthread_mutex_lock(&mutex);
}
static inline void Mutex_Unlock(THEORAPLAY_MUTEX_T mutex)
{
pthread_mutex_unlock(&mutex);
}
#endif
static int FeedMoreOggData(THEORAPLAY_Io *io, ogg_sync_state *sync)
{
long buflen = 4096;
char *buffer = ogg_sync_buffer(sync, buflen);
if (buffer == NULL)
return -1;
buflen = io->read(io, buffer, buflen);
if (buflen <= 0)
return 0;
return (ogg_sync_wrote(sync, buflen) == 0) ? 1 : -1;
} // FeedMoreOggData
// This massive function is where all the effort happens.
static void WorkerThread(TheoraDecoder *ctx)
{
// make sure we initialized the stream before using pagein, but the stream
// will know to ignore pages that aren't meant for it, so pass to both.
#define queue_ogg_page(ctx) do { \
if (tpackets) ogg_stream_pagein(&tstream, &page); \
if (vpackets) ogg_stream_pagein(&vstream, &page); \
} while (0)
unsigned long audioframes = 0;
unsigned long videoframes = 0;
double fps = 0.0;
int was_error = 1; // resets to 0 at the end.
int eos = 0; // end of stream flag.
// Too much Ogg/Vorbis/Theora state...
ogg_packet packet;
ogg_sync_state sync;
ogg_page page;
int vpackets = 0;
vorbis_info vinfo;
vorbis_comment vcomment;
ogg_stream_state vstream;
int vdsp_init = 0;
vorbis_dsp_state vdsp;
int tpackets = 0;
th_info tinfo;
th_comment tcomment;
ogg_stream_state tstream;
int vblock_init = 0;
vorbis_block vblock;
th_dec_ctx *tdec = NULL;
th_setup_info *tsetup = NULL;
ogg_sync_init(&sync);
vorbis_info_init(&vinfo);
vorbis_comment_init(&vcomment);
th_comment_init(&tcomment);
th_info_init(&tinfo);
int bos = 1;
while (!ctx->halt && bos)
{
if (FeedMoreOggData(ctx->io, &sync) <= 0)
goto cleanup;
// parse out the initial header.
while ( (!ctx->halt) && (ogg_sync_pageout(&sync, &page) > 0) )
{
ogg_stream_state test;
if (!ogg_page_bos(&page)) // not a header.
{
queue_ogg_page(ctx);
bos = 0;
break;
} // if
ogg_stream_init(&test, ogg_page_serialno(&page));
ogg_stream_pagein(&test, &page);
ogg_stream_packetout(&test, &packet);
if (!tpackets && (th_decode_headerin(&tinfo, &tcomment, &tsetup, &packet) >= 0))
{
memcpy(&tstream, &test, sizeof (test));
tpackets = 1;
} // if
else if (!vpackets && (vorbis_synthesis_headerin(&vinfo, &vcomment, &packet) >= 0))
{
memcpy(&vstream, &test, sizeof (test));
vpackets = 1;
} // else if
else
{
// whatever it is, we don't care about it
ogg_stream_clear(&test);
} // else
} // while
} // while
// no audio OR video?
if (ctx->halt || (!vpackets && !tpackets))
goto cleanup;
// apparently there are two more theora and two more vorbis headers next.
while ((!ctx->halt) && ((tpackets && (tpackets < 3)) || (vpackets && (vpackets < 3))))
{
while (!ctx->halt && tpackets && (tpackets < 3))
{
if (ogg_stream_packetout(&tstream, &packet) != 1)
break; // get more data?
if (!th_decode_headerin(&tinfo, &tcomment, &tsetup, &packet))
goto cleanup;
tpackets++;
} // while
while (!ctx->halt && vpackets && (vpackets < 3))
{
if (ogg_stream_packetout(&vstream, &packet) != 1)
break; // get more data?
if (vorbis_synthesis_headerin(&vinfo, &vcomment, &packet))
goto cleanup;
vpackets++;
} // while
// get another page, try again?
if (ogg_sync_pageout(&sync, &page) > 0)
queue_ogg_page(ctx);
else if (FeedMoreOggData(ctx->io, &sync) <= 0)
goto cleanup;
} // while
// okay, now we have our streams, ready to set up decoding.
if (!ctx->halt && tpackets)
{
// th_decode_alloc() docs say to check for insanely large frames yourself.
if ((tinfo.frame_width > 99999) || (tinfo.frame_height > 99999))
goto cleanup;
// We treat "unspecified" as NTSC. *shrug*
if ( (tinfo.colorspace != TH_CS_UNSPECIFIED) &&
(tinfo.colorspace != TH_CS_ITU_REC_470M) &&
(tinfo.colorspace != TH_CS_ITU_REC_470BG) )
{
assert(0 && "Unsupported colorspace."); // !!! FIXME
goto cleanup;
} // if
if (tinfo.pixel_fmt != TH_PF_420) { assert(0); goto cleanup; } // !!! FIXME
if (tinfo.fps_denominator != 0)
fps = ((double) tinfo.fps_numerator) / ((double) tinfo.fps_denominator);
tdec = th_decode_alloc(&tinfo, tsetup);
if (!tdec) goto cleanup;
// Set decoder to maximum post-processing level.
// Theoretically we could try dropping this level if we're not keeping up.
int pp_level_max = 0;
// !!! FIXME: maybe an API to set this?
//th_decode_ctl(tdec, TH_DECCTL_GET_PPLEVEL_MAX, &pp_level_max, sizeof(pp_level_max));
th_decode_ctl(tdec, TH_DECCTL_SET_PPLEVEL, &pp_level_max, sizeof(pp_level_max));
} // if
// Done with this now.
if (tsetup != NULL)
{
th_setup_free(tsetup);
tsetup = NULL;
} // if
if (!ctx->halt && vpackets)
{
vdsp_init = (vorbis_synthesis_init(&vdsp, &vinfo) == 0);
if (!vdsp_init)
goto cleanup;
vblock_init = (vorbis_block_init(&vdsp, &vblock) == 0);
if (!vblock_init)
goto cleanup;
} // if
// Now we can start the actual decoding!
// Note that audio and video don't _HAVE_ to start simultaneously.
Mutex_Lock(ctx->lock);
ctx->prepped = 1;
ctx->hasvideo = (tpackets != 0);
ctx->hasaudio = (vpackets != 0);
Mutex_Unlock(ctx->lock);
while (!ctx->halt && !eos)
{
int need_pages = 0; // need more Ogg pages?
int saw_video_frame = 0;
// Try to read as much audio as we can at once. We limit the outer
// loop to one video frame and as much audio as we can eat.
while (!ctx->halt && vpackets)
{
float **pcm = NULL;
const int frames = vorbis_synthesis_pcmout(&vdsp, &pcm);
if (frames > 0)
{
const int channels = vinfo.channels;
int chanidx, frameidx;
float *samples;
AudioPacket *item = (AudioPacket *) malloc(sizeof (AudioPacket));
if (item == NULL) goto cleanup;
item->playms = (unsigned long) ((((double) audioframes) / ((double) vinfo.rate)) * 1000.0);
item->channels = channels;
item->freq = vinfo.rate;
item->frames = frames;
item->samples = (float *) malloc(sizeof (float) * frames * channels);
item->next = NULL;
if (item->samples == NULL)
{
free(item);
goto cleanup;
} // if
// I bet this beats the crap out of the CPU cache...
samples = item->samples;
for (frameidx = 0; frameidx < frames; frameidx++)
{
for (chanidx = 0; chanidx < channels; chanidx++)
*(samples++) = pcm[chanidx][frameidx];
} // for
vorbis_synthesis_read(&vdsp, frames); // we ate everything.
audioframes += frames;
//printf("Decoded %d frames of audio.\n", (int) frames);
Mutex_Lock(ctx->lock);
ctx->audioms += item->playms;
if (ctx->audiolisttail)
{
assert(ctx->audiolist);
ctx->audiolisttail->next = item;
} // if
else
{
assert(!ctx->audiolist);
ctx->audiolist = item;
} // else
ctx->audiolisttail = item;
Mutex_Unlock(ctx->lock);
} // if
else // no audio available left in current packet?
{
// try to feed another packet to the Vorbis stream...
if (ogg_stream_packetout(&vstream, &packet) <= 0)
{
if (!tpackets)
need_pages = 1; // no video, get more pages now.
break; // we'll get more pages when the video catches up.
} // if
else
{
if (vorbis_synthesis(&vblock, &packet) == 0)
vorbis_synthesis_blockin(&vdsp, &vblock);
} // else
} // else
} // while
if (!ctx->halt && tpackets)
{
// Theora, according to example_player.c, is
// "one [packet] in, one [frame] out."
if (ogg_stream_packetout(&tstream, &packet) <= 0)
need_pages = 1;
else
{
ogg_int64_t granulepos = 0;
const int rc = th_decode_packetin(tdec, &packet, &granulepos);
if (rc == TH_DUPFRAME)
videoframes++; // nothing else to do.
else if (rc == 0) // new frame!
{
th_ycbcr_buffer ycbcr;
if (th_decode_ycbcr_out(tdec, ycbcr) == 0)
{
VideoFrame *item = (VideoFrame *) malloc(sizeof (VideoFrame));
if (item == NULL) goto cleanup;
item->playms = (fps == 0) ? 0 : (unsigned int) ((((double) videoframes) / fps) * 1000.0);
item->fps = fps;
item->width = tinfo.pic_width;
item->height = tinfo.pic_height;
item->format = ctx->vidfmt;
item->pixels = ctx->vidcvt(&tinfo, ycbcr);
item->next = NULL;
if (item->pixels == NULL)
{
free(item);
goto cleanup;
} // if
//printf("Decoded another video frame.\n");
Mutex_Lock(ctx->lock);
if (ctx->videolisttail)
{
assert(ctx->videolist);
ctx->videolisttail->next = item;
} // if
else
{
assert(!ctx->videolist);
ctx->videolist = item;
} // else
ctx->videolisttail = item;
ctx->videocount++;
Mutex_Unlock(ctx->lock);
saw_video_frame = 1;
} // if
videoframes++;
} // if
} // else
} // if
if (!ctx->halt && need_pages)
{
const int rc = FeedMoreOggData(ctx->io, &sync);
if (rc == 0)
eos = 1; // end of stream
else if (rc < 0)
goto cleanup; // i/o error, etc.
else
{
while (!ctx->halt && (ogg_sync_pageout(&sync, &page) > 0))
queue_ogg_page(ctx);
} // else
} // if
// Sleep the process until we have space for more frames.
if (saw_video_frame)
{
int go_on = !ctx->halt;
//printf("Sleeping.\n");
while (go_on)
{
// !!! FIXME: This is stupid. I should use a semaphore for this.
Mutex_Lock(ctx->lock);
go_on = !ctx->halt && (ctx->videocount >= ctx->maxframes);
Mutex_Unlock(ctx->lock);
if (go_on)
sleepms(10);
} // while
//printf("Awake!\n");
} // if
} // while
was_error = 0;
cleanup:
ctx->decode_error = (!ctx->halt && was_error);
if (tdec != NULL) th_decode_free(tdec);
if (tsetup != NULL) th_setup_free(tsetup);
if (vblock_init) vorbis_block_clear(&vblock);
if (vdsp_init) vorbis_dsp_clear(&vdsp);
if (tpackets) ogg_stream_clear(&tstream);
if (vpackets) ogg_stream_clear(&vstream);
th_info_clear(&tinfo);
th_comment_clear(&tcomment);
vorbis_comment_clear(&vcomment);
vorbis_info_clear(&vinfo);
ogg_sync_clear(&sync);
ctx->io->close(ctx->io);
ctx->thread_done = 1;
} // WorkerThread
static void *WorkerThreadEntry(void *_this)
{
TheoraDecoder *ctx = (TheoraDecoder *) _this;
WorkerThread(ctx);
//printf("Worker thread is done.\n");
return NULL;
} // WorkerThreadEntry
static long IoFopenRead(THEORAPLAY_Io *io, void *buf, long buflen)
{
SDL_RWops *f = (SDL_RWops *) io->userdata;
const size_t br = SDL_RWread(f, buf, 1, buflen);
if (br == 0)
return -1;
return (long) br;
} // IoFopenRead
static void IoFopenClose(THEORAPLAY_Io *io)
{
SDL_RWops *f = (SDL_RWops *) io->userdata;
SDL_RWclose(f);
free(io);
} // IoFopenClose
THEORAPLAY_Decoder *THEORAPLAY_startDecodeFile(const char *fname,
const unsigned int maxframes,
THEORAPLAY_VideoFormat vidfmt)
{
THEORAPLAY_Io *io = (THEORAPLAY_Io *) malloc(sizeof (THEORAPLAY_Io));
if (io == NULL)
return NULL;
SDL_RWops *f = SDL_RWFromFile(fname, "rb");
if (f == NULL)
{
free(io);
return NULL;
} // if
io->read = IoFopenRead;
io->close = IoFopenClose;
io->userdata = f;
return THEORAPLAY_startDecode(io, maxframes, vidfmt);
} // THEORAPLAY_startDecodeFile
THEORAPLAY_Decoder *THEORAPLAY_startDecode(THEORAPLAY_Io *io,
const unsigned int maxframes,
THEORAPLAY_VideoFormat vidfmt)
{
TheoraDecoder *ctx = NULL;
ConvertVideoFrameFn vidcvt = NULL;
switch (vidfmt)
{
// !!! FIXME: current expects TH_PF_420.
#define VIDCVT(t) case THEORAPLAY_VIDFMT_##t: vidcvt = ConvertVideoFrame420To##t; break;
VIDCVT(YV12)
VIDCVT(IYUV)
VIDCVT(RGB)
VIDCVT(RGBA)
#undef VIDCVT
default: goto startdecode_failed; // invalid/unsupported format.
} // switch
ctx = (TheoraDecoder *) malloc(sizeof (TheoraDecoder));
if (ctx == NULL)
goto startdecode_failed;
memset(ctx, '\0', sizeof (TheoraDecoder));
ctx->maxframes = maxframes;
ctx->vidfmt = vidfmt;
ctx->vidcvt = vidcvt;
ctx->io = io;
if (Mutex_Create(ctx) == 0)
{
ctx->thread_created = (Thread_Create(ctx, WorkerThreadEntry) == 0);
if (ctx->thread_created)
return (THEORAPLAY_Decoder *) ctx;
} // if
Mutex_Destroy(ctx->lock);
startdecode_failed:
io->close(io);
free(ctx);
return NULL;
} // THEORAPLAY_startDecode
void THEORAPLAY_stopDecode(THEORAPLAY_Decoder *decoder)
{
TheoraDecoder *ctx = (TheoraDecoder *) decoder;
if (!ctx)
return;
if (ctx->thread_created)
{
ctx->halt = 1;
Thread_Join(ctx->worker);
Mutex_Destroy(ctx->lock);
} // if
VideoFrame *videolist = ctx->videolist;
while (videolist)
{
VideoFrame *next = videolist->next;
free(videolist->pixels);
free(videolist);
videolist = next;
} // while
AudioPacket *audiolist = ctx->audiolist;
while (audiolist)
{
AudioPacket *next = audiolist->next;
free(audiolist->samples);
free(audiolist);
audiolist = next;
} // while
free(ctx);
} // THEORAPLAY_stopDecode
int THEORAPLAY_isDecoding(THEORAPLAY_Decoder *decoder)
{
TheoraDecoder *ctx = (TheoraDecoder *) decoder;
int retval = 0;
if (ctx)
{
Mutex_Lock(ctx->lock);
retval = ( ctx && (ctx->audiolist || ctx->videolist ||
(ctx->thread_created && !ctx->thread_done)) );
Mutex_Unlock(ctx->lock);
} // if
return retval;
} // THEORAPLAY_isDecoding
#define GET_SYNCED_VALUE(typ, defval, decoder, member) \
TheoraDecoder *ctx = (TheoraDecoder *) decoder; \
typ retval = defval; \
if (ctx) { \
Mutex_Lock(ctx->lock); \
retval = ctx->member; \
Mutex_Unlock(ctx->lock); \
} \
return retval;
int THEORAPLAY_isInitialized(THEORAPLAY_Decoder *decoder)
{
GET_SYNCED_VALUE(int, 0, decoder, prepped);
} // THEORAPLAY_isInitialized
int THEORAPLAY_hasVideoStream(THEORAPLAY_Decoder *decoder)
{
GET_SYNCED_VALUE(int, 0, decoder, hasvideo);
} // THEORAPLAY_hasVideoStream
int THEORAPLAY_hasAudioStream(THEORAPLAY_Decoder *decoder)
{
GET_SYNCED_VALUE(int, 0, decoder, hasaudio);
} // THEORAPLAY_hasAudioStream
unsigned int THEORAPLAY_availableVideo(THEORAPLAY_Decoder *decoder)
{
GET_SYNCED_VALUE(unsigned int, 0, decoder, videocount);
} // THEORAPLAY_hasAudioStream
unsigned int THEORAPLAY_availableAudio(THEORAPLAY_Decoder *decoder)
{
GET_SYNCED_VALUE(unsigned int, 0, decoder, audioms);
} // THEORAPLAY_hasAudioStream
int THEORAPLAY_decodingError(THEORAPLAY_Decoder *decoder)
{
GET_SYNCED_VALUE(int, 0, decoder, decode_error);
} // THEORAPLAY_decodingError
const THEORAPLAY_AudioPacket *THEORAPLAY_getAudio(THEORAPLAY_Decoder *decoder)
{
TheoraDecoder *ctx = (TheoraDecoder *) decoder;
AudioPacket *retval;
Mutex_Lock(ctx->lock);
retval = ctx->audiolist;
if (retval)
{
ctx->audioms -= retval->playms;
ctx->audiolist = retval->next;
retval->next = NULL;
if (ctx->audiolist == NULL)
ctx->audiolisttail = NULL;
} // if
Mutex_Unlock(ctx->lock);
return retval;
} // THEORAPLAY_getAudio
void THEORAPLAY_freeAudio(const THEORAPLAY_AudioPacket *_item)
{
THEORAPLAY_AudioPacket *item = (THEORAPLAY_AudioPacket *) _item;
if (item != NULL)
{
assert(item->next == NULL);
free(item->samples);
free(item);
} // if
} // THEORAPLAY_freeAudio
const THEORAPLAY_VideoFrame *THEORAPLAY_getVideo(THEORAPLAY_Decoder *decoder)
{
TheoraDecoder *ctx = (TheoraDecoder *) decoder;
VideoFrame *retval;
Mutex_Lock(ctx->lock);
retval = ctx->videolist;
if (retval)
{
ctx->videolist = retval->next;
retval->next = NULL;
if (ctx->videolist == NULL)
ctx->videolisttail = NULL;
assert(ctx->videocount > 0);
ctx->videocount--;
} // if
Mutex_Unlock(ctx->lock);
return retval;
} // THEORAPLAY_getVideo
void THEORAPLAY_freeVideo(const THEORAPLAY_VideoFrame *_item)
{
THEORAPLAY_VideoFrame *item = (THEORAPLAY_VideoFrame *) _item;
if (item != NULL)
{
assert(item->next == NULL);
free(item->pixels);
free(item);
} // if
} // THEORAPLAY_freeVideo
// end of theoraplay.cpp ...

View File

@@ -0,0 +1,85 @@
/**
* TheoraPlay; multithreaded Ogg Theora/Ogg Vorbis decoding.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
#ifndef _INCL_THEORAPLAY_H_
#define _INCL_THEORAPLAY_H_
#ifdef __cplusplus
extern "C" {
#endif
typedef struct THEORAPLAY_Io THEORAPLAY_Io;
struct THEORAPLAY_Io
{
long (*read)(THEORAPLAY_Io *io, void *buf, long buflen);
void (*close)(THEORAPLAY_Io *io);
void *userdata;
};
typedef struct THEORAPLAY_Decoder THEORAPLAY_Decoder;
/* YV12 is YCrCb, not YCbCr; that's what SDL uses for YV12 overlays. */
typedef enum THEORAPLAY_VideoFormat
{
THEORAPLAY_VIDFMT_YV12, /* NTSC colorspace, planar YCrCb 4:2:0 */
THEORAPLAY_VIDFMT_IYUV, /* NTSC colorspace, planar YCbCr 4:2:0 */
THEORAPLAY_VIDFMT_RGB, /* 24 bits packed pixel RGB */
THEORAPLAY_VIDFMT_RGBA /* 32 bits packed pixel RGBA (full alpha). */
} THEORAPLAY_VideoFormat;
typedef struct THEORAPLAY_VideoFrame
{
unsigned int playms;
double fps;
unsigned int width;
unsigned int height;
THEORAPLAY_VideoFormat format;
unsigned char *pixels;
struct THEORAPLAY_VideoFrame *next;
} THEORAPLAY_VideoFrame;
typedef struct THEORAPLAY_AudioPacket
{
unsigned int playms; /* playback start time in milliseconds. */
int channels;
int freq;
int frames;
float *samples; /* frames * channels float32 samples. */
struct THEORAPLAY_AudioPacket *next;
} THEORAPLAY_AudioPacket;
THEORAPLAY_Decoder *THEORAPLAY_startDecodeFile(const char *fname,
const unsigned int maxframes,
THEORAPLAY_VideoFormat vidfmt);
THEORAPLAY_Decoder *THEORAPLAY_startDecode(THEORAPLAY_Io *io,
const unsigned int maxframes,
THEORAPLAY_VideoFormat vidfmt);
void THEORAPLAY_stopDecode(THEORAPLAY_Decoder *decoder);
int THEORAPLAY_isDecoding(THEORAPLAY_Decoder *decoder);
int THEORAPLAY_decodingError(THEORAPLAY_Decoder *decoder);
int THEORAPLAY_isInitialized(THEORAPLAY_Decoder *decoder);
int THEORAPLAY_hasVideoStream(THEORAPLAY_Decoder *decoder);
int THEORAPLAY_hasAudioStream(THEORAPLAY_Decoder *decoder);
unsigned int THEORAPLAY_availableVideo(THEORAPLAY_Decoder *decoder);
unsigned int THEORAPLAY_availableAudio(THEORAPLAY_Decoder *decoder);
const THEORAPLAY_AudioPacket *THEORAPLAY_getAudio(THEORAPLAY_Decoder *decoder);
void THEORAPLAY_freeAudio(const THEORAPLAY_AudioPacket *item);
const THEORAPLAY_VideoFrame *THEORAPLAY_getVideo(THEORAPLAY_Decoder *decoder);
void THEORAPLAY_freeVideo(const THEORAPLAY_VideoFrame *item);
#ifdef __cplusplus
}
#endif
#endif /* include-once blocker. */
/* end of theoraplay.h ... */

View File

@@ -0,0 +1,74 @@
/**
* TheoraPlay; multithreaded Ogg Theora/Ogg Vorbis decoding.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
#if !THEORAPLAY_INTERNAL
#error Do not include this in your app. It is used internally by TheoraPlay.
#endif
static unsigned char *THEORAPLAY_CVT_FNNAME_420(const th_info *tinfo,
const th_ycbcr_buffer ycbcr)
{
const int w = tinfo->pic_width;
const int h = tinfo->pic_height;
unsigned char *pixels = (unsigned char *) malloc(w * h * 4);
if (pixels)
{
unsigned char *dst = pixels;
const int ystride = ycbcr[0].stride;
const int cbstride = ycbcr[1].stride;
const int crstride = ycbcr[2].stride;
const int yoff = (tinfo->pic_x & ~1) + ystride * (tinfo->pic_y & ~1);
const int cboff = (tinfo->pic_x / 2) + (cbstride) * (tinfo->pic_y / 2);
const unsigned char *py = ycbcr[0].data + yoff;
const unsigned char *pcb = ycbcr[1].data + cboff;
const unsigned char *pcr = ycbcr[2].data + cboff;
int posx, posy;
for (posy = 0; posy < h; posy++)
{
for (posx = 0; posx < w; posx++)
{
// http://www.theora.org/doc/Theora.pdf, 1.1 spec,
// chapter 4.2 (Y'CbCr -> Y'PbPr -> R'G'B')
// These constants apparently work for NTSC _and_ PAL/SECAM.
const float yoffset = 16.0f;
const float yexcursion = 219.0f;
const float cboffset = 128.0f;
const float cbexcursion = 224.0f;
const float croffset = 128.0f;
const float crexcursion = 224.0f;
const float kr = 0.299f;
const float kb = 0.114f;
const float y = (((float) py[posx]) - yoffset) / yexcursion;
const float pb = (((float) pcb[posx / 2]) - cboffset) / cbexcursion;
const float pr = (((float) pcr[posx / 2]) - croffset) / crexcursion;
const float r = (y + (2.0f * (1.0f - kr) * pr)) * 255.0f;
const float g = (y - ((2.0f * (((1.0f - kb) * kb) / ((1.0f - kb) - kr))) * pb) - ((2.0f * (((1.0f - kr) * kr) / ((1.0f - kb) - kr))) * pr)) * 255.0f;
const float b = (y + (2.0f * (1.0f - kb) * pb)) * 255.0f;
*(dst++) = (unsigned char) ((r < 0.0f) ? 0.0f : (r > 255.0f) ? 255.0f : r);
*(dst++) = (unsigned char) ((g < 0.0f) ? 0.0f : (g > 255.0f) ? 255.0f : g);
*(dst++) = (unsigned char) ((b < 0.0f) ? 0.0f : (b > 255.0f) ? 255.0f : b);
#if THEORAPLAY_CVT_RGB_ALPHA
*(dst++) = 0xFF;
#endif
} // for
// adjust to the start of the next line.
py += ystride;
pcb += cbstride * (posy % 2);
pcr += crstride * (posy % 2);
} // for
} // if
return pixels;
} // THEORAPLAY_CVT_FNNAME_420
// end of theoraplay_cvtrgb.h ...