Compare commits
10 commits
c7572e3d55
...
4084e5007e
Author | SHA1 | Date | |
---|---|---|---|
Epicalert | 4084e5007e | ||
Epicalert | 293154034e | ||
Epicalert | fe75856701 | ||
Epicalert | 972a0cc9b9 | ||
Epicalert | 678393ccb1 | ||
Epicalert | f077ca5b5f | ||
Epicalert | 57ad5cd47a | ||
Epicalert | c06f6beae6 | ||
Epicalert | eb4bf55cda | ||
Epicalert | 583ec11ee5 |
|
@ -47,6 +47,7 @@ file(
|
||||||
DESTINATION
|
DESTINATION
|
||||||
${PROJECT_BINARY_DIR} )
|
${PROJECT_BINARY_DIR} )
|
||||||
file( MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/models )
|
file( MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/models )
|
||||||
|
pack_model( "default" )
|
||||||
pack_model( "test" )
|
pack_model( "test" )
|
||||||
pack_model( "rms" )
|
pack_model( "rms" )
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
|
@ -69,6 +70,7 @@ add_executable( fc2d
|
||||||
src/error.cpp
|
src/error.cpp
|
||||||
src/eye.cpp
|
src/eye.cpp
|
||||||
src/configfile.cpp
|
src/configfile.cpp
|
||||||
|
src/input.cpp
|
||||||
packaging/fc2d.rc
|
packaging/fc2d.rc
|
||||||
)
|
)
|
||||||
target_link_libraries( fc2d ${OpenCV_LIBS} ${OPENGL_LIBRARIES} ${WEBP_LIBRARIES}
|
target_link_libraries( fc2d ${OpenCV_LIBS} ${OPENGL_LIBRARIES} ${WEBP_LIBRARIES}
|
||||||
|
|
7
TODO.md
|
@ -5,10 +5,9 @@
|
||||||
- demo video?
|
- demo video?
|
||||||
|
|
||||||
## models
|
## models
|
||||||
- better default model
|
- clipping (alpha stencil)
|
||||||
|
|
||||||
## Bugs
|
## Bugs
|
||||||
- crash when eye goes outside of roi
|
|
||||||
- incorrect appdata location on Windows
|
- incorrect appdata location on Windows
|
||||||
|
|
||||||
# Whenever
|
# Whenever
|
||||||
|
@ -19,3 +18,7 @@
|
||||||
|
|
||||||
## vision
|
## vision
|
||||||
- eye open/closed
|
- eye open/closed
|
||||||
|
|
||||||
|
## graphics
|
||||||
|
- in-window text rendering
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
.TH FC2D_MODEL 5 "Facecam2D Model Format 0.3 Manual"
|
.TH FC2D_MODEL 5 "Facecam2D Model Format 0.5 Manual"
|
||||||
.
|
.
|
||||||
.SH NAME
|
.SH NAME
|
||||||
fc2d_model \- Facecam2D model file format
|
fc2d_model \- Facecam2D model file format
|
||||||
|
@ -65,6 +65,23 @@ Unnecessary files should not be present in the archive, but archives with
|
||||||
unnecessary files are still valid.
|
unnecessary files are still valid.
|
||||||
.
|
.
|
||||||
.
|
.
|
||||||
|
.SH FORMAT VERSION
|
||||||
|
.PP
|
||||||
|
.
|
||||||
|
Every Facecam2D model has a version code in the
|
||||||
|
.B model.toml
|
||||||
|
in the format "major.minor".
|
||||||
|
A given version of Facecam2D can read model files with the same
|
||||||
|
major version as the version it is compatible with.
|
||||||
|
For example, if Facecam2D can read the model format up to version 0.3,
|
||||||
|
the same build of Facecam2D can also read a model written in format 0.2,
|
||||||
|
but cannot play a model written in format 1.2 or 0.5.
|
||||||
|
It is only backwards compatible with the same major version.
|
||||||
|
Higher minor versions of the model format indicate that there are newer
|
||||||
|
optional features available, but does not break compatibility with formats
|
||||||
|
of the same major version.
|
||||||
|
.
|
||||||
|
.
|
||||||
.SH KEYS
|
.SH KEYS
|
||||||
.SS format
|
.SS format
|
||||||
.TP
|
.TP
|
||||||
|
@ -85,6 +102,27 @@ String: The name of the model (will be displayed in the window title).
|
||||||
version
|
version
|
||||||
String: A version string for the model (currently unused).
|
String: A version string for the model (currently unused).
|
||||||
.
|
.
|
||||||
|
.TP
|
||||||
|
artist
|
||||||
|
.ft B
|
||||||
|
(Since version 0.5)
|
||||||
|
.ft
|
||||||
|
String: Name of artist or photographer
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
modeler
|
||||||
|
.ft B
|
||||||
|
(Since version 0.5)
|
||||||
|
.ft
|
||||||
|
String: Name of modeler or rigger
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
license
|
||||||
|
.ft B
|
||||||
|
(Since version 0.5)
|
||||||
|
.ft
|
||||||
|
String: SPDX license identifier (if none model is assumed to be proprietary)
|
||||||
|
.
|
||||||
.
|
.
|
||||||
.SS part
|
.SS part
|
||||||
.TP
|
.TP
|
||||||
|
@ -114,6 +152,54 @@ Float: How far the part will move towards the
|
||||||
point.
|
point.
|
||||||
If negative, the part will move in the opposite direction.
|
If negative, the part will move in the opposite direction.
|
||||||
.
|
.
|
||||||
|
.TP
|
||||||
|
rot_factor, scale_factor
|
||||||
|
.ft B
|
||||||
|
(Since version 0.2)
|
||||||
|
.ft
|
||||||
|
Float: How much the part will rotate/scale.
|
||||||
|
Default is 1.0.
|
||||||
|
Higher values result in more exaggerated rotation or scaling.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
offset_factor
|
||||||
|
.ft B
|
||||||
|
(Since version 0.4)
|
||||||
|
.ft
|
||||||
|
float: How much the part will be moved by the offset bind.
|
||||||
|
See
|
||||||
|
.B offset_bind.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
origin
|
||||||
|
.ft B
|
||||||
|
(Since version 0.3)
|
||||||
|
.ft
|
||||||
|
Float Array: Origin (pivot point) of the model defined in
|
||||||
|
normalized coordinates.
|
||||||
|
Default is [0.0, 0.0].
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
pos_offset, scale_offset
|
||||||
|
.ft B
|
||||||
|
(Since version 0.3)
|
||||||
|
.ft
|
||||||
|
Float Array: Positional offset or scale of a part.
|
||||||
|
Default is [0.0, 0.0] for
|
||||||
|
.B pos_offset
|
||||||
|
and [1.0, 1.0] for
|
||||||
|
.B scale_offset.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
offset_bind
|
||||||
|
.ft B
|
||||||
|
(Since version 0.4)
|
||||||
|
.ft
|
||||||
|
String: Offsets are vectors instead of actual points on the image.
|
||||||
|
You can bind to an offset with this property, which will
|
||||||
|
offset the part's position according to the vector.
|
||||||
|
Possible values: offset-eyes
|
||||||
|
.
|
||||||
.SS textures
|
.SS textures
|
||||||
.TP
|
.TP
|
||||||
file
|
file
|
||||||
|
|
BIN
models/default/back hair.webp
Normal file
After Width: | Height: | Size: 101 KiB |
BIN
models/default/body.webp
Normal file
After Width: | Height: | Size: 45 KiB |
BIN
models/default/clothes.webp
Normal file
After Width: | Height: | Size: 61 KiB |
BIN
models/default/collar.webp
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
models/default/eye brows.webp
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
models/default/eye lids.webp
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
models/default/eye.webp
Normal file
After Width: | Height: | Size: 35 KiB |
BIN
models/default/front hair.webp
Normal file
After Width: | Height: | Size: 68 KiB |
BIN
models/default/head.webp
Normal file
After Width: | Height: | Size: 57 KiB |
BIN
models/default/iris.webp
Normal file
After Width: | Height: | Size: 26 KiB |
91
models/default/model.toml
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
[format]
|
||||||
|
version_major = 0
|
||||||
|
version_minor = 4
|
||||||
|
|
||||||
|
[model_info]
|
||||||
|
name = "Default-chan"
|
||||||
|
version = "1.0"
|
||||||
|
|
||||||
|
|
||||||
|
[[part]]
|
||||||
|
texture = "back hair.webp"
|
||||||
|
bind = "head"
|
||||||
|
follow = "face"
|
||||||
|
factor = -0.1
|
||||||
|
|
||||||
|
[[part]]
|
||||||
|
texture = "body.webp"
|
||||||
|
bind = "head"
|
||||||
|
rot_factor=0.3
|
||||||
|
|
||||||
|
[[part]]
|
||||||
|
texture = "clothes.webp"
|
||||||
|
bind = "head"
|
||||||
|
follow = "face"
|
||||||
|
factor = 0.1
|
||||||
|
rot_factor=0.3
|
||||||
|
|
||||||
|
[[part]]
|
||||||
|
texture = "collar.webp"
|
||||||
|
bind = "head"
|
||||||
|
rot_factor=0.3
|
||||||
|
|
||||||
|
[[part]]
|
||||||
|
texture = "head.webp"
|
||||||
|
bind = "head"
|
||||||
|
|
||||||
|
[[part]]
|
||||||
|
texture = "nose.webp"
|
||||||
|
bind = "head"
|
||||||
|
follow = "face"
|
||||||
|
factor = 0.35
|
||||||
|
|
||||||
|
[[part]]
|
||||||
|
texture = "eye.webp"
|
||||||
|
bind = "head"
|
||||||
|
follow = "face"
|
||||||
|
factor = 0.35
|
||||||
|
|
||||||
|
[[part]]
|
||||||
|
texture = "iris.webp"
|
||||||
|
bind = "head"
|
||||||
|
follow = "face"
|
||||||
|
factor = 0.35
|
||||||
|
offset_bind = "offset-eyes"
|
||||||
|
offset_factor = 0.03
|
||||||
|
|
||||||
|
[[part]]
|
||||||
|
texture = "eye lids.webp"
|
||||||
|
bind = "head"
|
||||||
|
follow = "face"
|
||||||
|
factor = 0.35
|
||||||
|
|
||||||
|
[[part]]
|
||||||
|
bind = "head"
|
||||||
|
follow = "face"
|
||||||
|
factor = 0.35
|
||||||
|
|
||||||
|
[[part.textures]]
|
||||||
|
file = "mouth closed.webp"
|
||||||
|
|
||||||
|
[[part.textures]]
|
||||||
|
file = "mouth open.webp"
|
||||||
|
trigger = "mouth-open"
|
||||||
|
|
||||||
|
[[part]]
|
||||||
|
texture = "side hair.webp"
|
||||||
|
bind = "head"
|
||||||
|
follow = "face"
|
||||||
|
factor = 0.1
|
||||||
|
|
||||||
|
[[part]]
|
||||||
|
texture = "front hair.webp"
|
||||||
|
bind = "head"
|
||||||
|
follow = "face"
|
||||||
|
factor = 0.2
|
||||||
|
|
||||||
|
[[part]]
|
||||||
|
texture = "eye brows.webp"
|
||||||
|
bind = "head"
|
||||||
|
follow = "face"
|
||||||
|
factor = 0.35
|
BIN
models/default/mouth closed.webp
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
models/default/mouth open.webp
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
models/default/nose.webp
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
models/default/side hair.webp
Normal file
After Width: | Height: | Size: 66 KiB |
BIN
models/default/source.kra
Normal file
|
@ -1,10 +1,13 @@
|
||||||
[format]
|
[format]
|
||||||
version_major = 0
|
version_major = 0
|
||||||
version_minor = 2
|
version_minor = 5
|
||||||
|
|
||||||
[model_info]
|
[model_info]
|
||||||
name = "Richard Stallman"
|
name = "Richard Stallman"
|
||||||
version = "1.0"
|
version = "1.1"
|
||||||
|
artist = "Ruben Rodriguez"
|
||||||
|
modeler = "Epicalert"
|
||||||
|
license = "CC-BY-4.0"
|
||||||
|
|
||||||
[[part]]
|
[[part]]
|
||||||
texture = "bg.webp"
|
texture = "bg.webp"
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
[format]
|
[format]
|
||||||
version_major = 0
|
version_major = 0
|
||||||
version_minor = 1
|
version_minor = 5
|
||||||
|
|
||||||
[model_info]
|
[model_info]
|
||||||
name = "Test Model"
|
name = "Test Model"
|
||||||
version = "1.0"
|
version = "1.0"
|
||||||
|
artist = "Epicalert"
|
||||||
|
modeler = "Epicalert"
|
||||||
|
license = "GPL-3.0-only"
|
||||||
|
|
||||||
[[part]]
|
[[part]]
|
||||||
texture = "head-base.png"
|
texture = "head-base.png"
|
||||||
|
|
|
@ -36,7 +36,7 @@ struct optData optData = {
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
"test",
|
"default",
|
||||||
0,
|
0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -170,13 +170,20 @@ void cvFrame() {
|
||||||
cv::rectangle(frame, eyeRect, cv::Scalar(255, 255, 255));
|
cv::rectangle(frame, eyeRect, cv::Scalar(255, 255, 255));
|
||||||
|
|
||||||
cv::Mat eyeROI;
|
cv::Mat eyeROI;
|
||||||
|
glm::vec2 eyeVector(0,0);
|
||||||
|
|
||||||
|
// prevent assertion failed when eye is partly outside of image
|
||||||
|
if(eyeRect.x < 0 || eyeRect.x >= gray.cols || eyeRect.y < 0 || eyeRect.y >= gray.rows ||
|
||||||
|
eyeRect.x + eyeRect.width >= gray.cols || eyeRect.y - eyeRect.height >= gray.rows) goto noeye;
|
||||||
|
|
||||||
eyeROI = gray(eyeRect);
|
eyeROI = gray(eyeRect);
|
||||||
|
|
||||||
glm::vec2 eyeVector(0,0);
|
|
||||||
if (!optData.noEyes) {
|
if (!optData.noEyes) {
|
||||||
eyeVector = eyeDirection(eyeROI); // run pupil tracking algorithm and get look direction
|
eyeVector = eyeDirection(eyeROI); // run pupil tracking algorithm and get look direction
|
||||||
}
|
}
|
||||||
|
|
||||||
|
noeye:
|
||||||
|
|
||||||
//send control information to graphics
|
//send control information to graphics
|
||||||
float faceSize = landmarks[biggestFace][14].x - landmarks[biggestFace][2].x;
|
float faceSize = landmarks[biggestFace][14].x - landmarks[biggestFace][2].x;
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,12 @@
|
||||||
#include <cv.hpp>
|
#include <cv.hpp>
|
||||||
#include <args.hpp>
|
#include <args.hpp>
|
||||||
#include <error.hpp>
|
#include <error.hpp>
|
||||||
|
#include <input.hpp>
|
||||||
|
|
||||||
|
#ifndef NO_GRAPHICAL_DIALOG
|
||||||
|
#include <boxer/boxer.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
GLuint shader; //standard shader program used for all elements
|
GLuint shader; //standard shader program used for all elements
|
||||||
GLuint transUniform; //location of the "transMatrix" transformation matrix uniform in the shader
|
GLuint transUniform; //location of the "transMatrix" transformation matrix uniform in the shader
|
||||||
|
@ -94,8 +100,12 @@ void initGraphics () {
|
||||||
char *argv[1] = {(char*)"fc2d"};
|
char *argv[1] = {(char*)"fc2d"};
|
||||||
|
|
||||||
glutInit(&argc, argv);
|
glutInit(&argc, argv);
|
||||||
glutCreateWindow(PROJECT_NAME);
|
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
|
||||||
glutInitWindowSize(512, 512);
|
glutInitWindowSize(512, 512);
|
||||||
|
glutCreateWindow(PROJECT_NAME);
|
||||||
|
|
||||||
|
// input callback
|
||||||
|
glutKeyboardFunc(keyInput);
|
||||||
|
|
||||||
glewExperimental = GL_TRUE;
|
glewExperimental = GL_TRUE;
|
||||||
glewInit();
|
glewInit();
|
||||||
|
@ -236,3 +246,7 @@ void updateModel(struct FaceData faceData) {
|
||||||
//tell FreeGLUT to schedule a screen update
|
//tell FreeGLUT to schedule a screen update
|
||||||
glutPostRedisplay();
|
glutPostRedisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void showModelInfo() {
|
||||||
|
boxer::show(model->getInfoString().c_str(), "Model info", boxer::Style::Info);
|
||||||
|
}
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
#include <glm/vec2.hpp>
|
#include <glm/vec2.hpp>
|
||||||
|
|
||||||
#include <cv.hpp>
|
#include <cv.hpp>
|
||||||
|
#include <model.hpp>
|
||||||
|
#include <modelpart.hpp>
|
||||||
|
|
||||||
extern GLuint transUniform;
|
extern GLuint transUniform;
|
||||||
extern float windowAspectRatio;
|
extern float windowAspectRatio;
|
||||||
|
@ -28,4 +30,6 @@ void printShaderCompileLog(GLuint shader);
|
||||||
|
|
||||||
void updateModel(struct FaceData faceData);
|
void updateModel(struct FaceData faceData);
|
||||||
|
|
||||||
|
void showModelInfo();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
12
src/input.cpp
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#include <input.hpp>
|
||||||
|
#include <graphics.hpp>
|
||||||
|
|
||||||
|
void keyInput(unsigned char key, int x, int y) {
|
||||||
|
switch(key) {
|
||||||
|
case 'i': // model info
|
||||||
|
showModelInfo();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
6
src/input.hpp
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
#ifndef INPUT_HPP
|
||||||
|
#define INPUT_HPP
|
||||||
|
|
||||||
|
void keyInput(unsigned char key, int x, int y);
|
||||||
|
|
||||||
|
#endif
|
|
@ -12,7 +12,7 @@
|
||||||
#define BUFFER_SIZE_TEXTURE 16777220 // 16 MiB
|
#define BUFFER_SIZE_TEXTURE 16777220 // 16 MiB
|
||||||
|
|
||||||
#define SUPPORTED_MODEL_MAJOR 0
|
#define SUPPORTED_MODEL_MAJOR 0
|
||||||
#define SUPPORTED_MODEL_MINOR 3
|
#define SUPPORTED_MODEL_MINOR 5
|
||||||
|
|
||||||
void textureFromArchive(zip_t* archive, const char* path, ModelPart* part, size_t slot, std::string triggerName) {
|
void textureFromArchive(zip_t* archive, const char* path, ModelPart* part, size_t slot, std::string triggerName) {
|
||||||
zip_file_t* textureFile = zip_fopen(archive, path, 0);
|
zip_file_t* textureFile = zip_fopen(archive, path, 0);
|
||||||
|
@ -90,7 +90,29 @@ Model::Model(const char* path) {
|
||||||
} else {
|
} else {
|
||||||
name = nameResult.second;
|
name = nameResult.second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get authors
|
||||||
|
// artist (or photographer if image)
|
||||||
|
auto artistResult = modelInfo->getString("artist");
|
||||||
|
|
||||||
|
if (artistResult.first) {
|
||||||
|
artist = artistResult.second;
|
||||||
}
|
}
|
||||||
|
// rigger/modeler
|
||||||
|
auto modelerResult = modelInfo->getString("modeler");
|
||||||
|
|
||||||
|
if (modelerResult.first) {
|
||||||
|
modeler = modelerResult.second;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get license (SPDX identifier, if not present file is assumed to be proprietary)
|
||||||
|
auto licenseResult = modelInfo->getString("license");
|
||||||
|
|
||||||
|
if (licenseResult.first) {
|
||||||
|
license = licenseResult.second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// parse parts
|
// parse parts
|
||||||
auto partsDescArray = modelDesc.table->getArray("part");
|
auto partsDescArray = modelDesc.table->getArray("part");
|
||||||
|
@ -212,3 +234,7 @@ std::string Model::getName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Model::getInfoString() {
|
||||||
|
return fmt::format("{}\n{}\n\nArtist: {}\nModeler: {}",
|
||||||
|
name, license, artist, modeler);
|
||||||
|
}
|
||||||
|
|
|
@ -10,6 +10,10 @@ class Model {
|
||||||
|
|
||||||
std::string name;
|
std::string name;
|
||||||
|
|
||||||
|
std::string artist;
|
||||||
|
std::string modeler;
|
||||||
|
std::string license;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Model(const char* path);
|
Model(const char* path);
|
||||||
|
|
||||||
|
@ -18,6 +22,7 @@ class Model {
|
||||||
void updateTransforms(struct FaceData faceData);
|
void updateTransforms(struct FaceData faceData);
|
||||||
|
|
||||||
std::string getName();
|
std::string getName();
|
||||||
|
std::string getInfoString();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|