Initial Commit
This commit is contained in:
5653
rcbasic_runtime/main.cpp
Normal file
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
1417
rcbasic_runtime/rc_defines.h
Normal file
File diff suppressed because it is too large
Load Diff
822
rcbasic_runtime/rc_geometry.h
Normal file
822
rcbasic_runtime/rc_geometry.h
Normal 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
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
6502
rcbasic_runtime/rc_media.h
Normal file
File diff suppressed because it is too large
Load Diff
23
rcbasic_runtime/rc_os_defines.h
Normal file
23
rcbasic_runtime/rc_os_defines.h
Normal 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
|
||||
555
rcbasic_runtime/rc_process.h
Normal file
555
rcbasic_runtime/rc_process.h
Normal 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
1556
rcbasic_runtime/rc_stdlib.h
Normal file
File diff suppressed because it is too large
Load Diff
1
rcbasic_runtime/resource.rc
Normal file
1
rcbasic_runtime/resource.rc
Normal file
@@ -0,0 +1 @@
|
||||
MAINICON ICON "rcbasic.ico"
|
||||
820
rcbasic_runtime/theoraplay.c
Normal file
820
rcbasic_runtime/theoraplay.c
Normal 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 ...
|
||||
|
||||
85
rcbasic_runtime/theoraplay.h
Normal file
85
rcbasic_runtime/theoraplay.h
Normal 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 ... */
|
||||
|
||||
74
rcbasic_runtime/theoraplay_cvtrgb.h
Normal file
74
rcbasic_runtime/theoraplay_cvtrgb.h
Normal 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 ...
|
||||
|
||||
Reference in New Issue
Block a user