I have a face model with 12 blendshapes where each blendshape is simply a list of float values between 0 (neutral facial expression) and 1 (maximum activated expression) but I am starting off with only the first 2 blendshapes; i.e. only two lists for now, say smile and skeptic looks.
My intention is to go through all the possible combinations of all the items in these two lists and make a footage (movie clip) of the facial movements, to display how all the possible combinations of blendshape values/weights look like.
So, I wrote the following to facilitate this scenario for only two blendshapes for now, and I save them to file as soon as the application closes:
public class BlendShapesVAL : MonoBehaviour
{
private List<float> _weightValues_Preset1_smile = new List<float>();
private List<float> _weightValues_Preset2_skeptic = new List<float>();
public bool _TransitionAnimation = true;
public float _TransitionAnimationSpeed = 2f;
public BlendShapesPresetController _BSPC;
private List<float> _weightsList = new List<float>();
public List<bool> _ActivationsList = new List<bool>();
public List<string> _PresetsNamesList = new List<string>();
private void Awake()
{
_weightsList.Clear();
_ActivationsList.Clear();
for (int i = 0; i < _PresetsNamesList.Count; ++i)
{
_ActivationsList.Add(false);
_weightsList.Add(0);
}
}
private void Start()
{
if (_BSPC != null)
{
// . . .
}
else
{
_BSPC = GetComponent<BlendShapesPresetController>();
}
StartCoroutine("Interpolate");
}
/// <summary>
/// Writes (i.e. saves) blendshape values to file when the application quits.
/// </summary>
///
private void OnApplicationQuit()
{
SaveBlendShapesValues(_weightValues_Preset1_smile);
SaveBlendShapesValues(_weightValues_Preset2_skeptic);
PlayerPrefs.DeleteAll();
}
/// <summary>
/// Goes thorugh all the possible combinations of blendshape weights.
/// For now, only the first two though!
/// </summary>
///
private IEnumerator Interpolate()
{
for (int i = 0; i <= 100; i++)
{
float weightValuesmile = (float)i / 100.0f;
_BSPC.SetWeight("Preset1_smile", weightValuesmile);
_weightValues_Preset1_smile.Add(weightValuesmile);
for (int j = 0; j <= 100; j++)
{
float weightValueSkeptic = (float)j / 100.0f;
_BSPC.SetWeight("Preset2_skeptic", weightValueSkeptic);
_weightValues_Preset2_skeptic.Add(weightValueSkeptic);
}
yield return null;
}
}
/// <summary>
/// Writes (i.e. saves) blendshape values to file.
/// <param name="blendShapesValuesFilePath">
/// The path to the file that will store the list of float values;
/// i.e. "Application.dataPath" plus the name of the CSV file.
/// </param>
/// <param name="values">
/// The float values that are the blendshape weights.
/// </param>
/// </summary>
///
private static void SaveBlendShapesValues(List<float> values)
{
List<string> lines = new List<string>
{
/// Add a header row for the labels.
"TimeStamp,Preset1_smile,Preset2_skeptic"
};
foreach (var value in values)
{
/// Iterate through all the elements.
lines.Add(DateTime.Now + "," + value);
}
/// Load the old counter.
int counter = PlayerPrefs.GetInt("_counter_", 0);
/// Concatenate the file name constituents and combine it with the application data path.
string fileName = string.Format("BlendShapesValues_{0}.csv", counter.ToString() );
string tempPath = Path.Combine(Application.dataPath, fileName);
try
{
File.WriteAllLines(tempPath, lines.ToArray() );
Debug.Log("Saved blendshape weight values to: " + tempPath);
/// Increment the counter.
counter++;
/// Save the current counter.
PlayerPrefs.SetInt("_counter_", counter);
PlayerPrefs.Save();
}
catch (Exception e)
{
Debug.LogWarning("Failed to save to PlayerPrefs: " + tempPath);
Debug.LogWarning("Error: " + e.Message);
}
}
}
In the Unity Editor, the blendshapes are displayed with values from 0 to 100, hence my conversion in the code, as seen in this screenshot:
The first file has 101 values (0...100 plus a top row for column labels) a snippet can be seen in this screenshot:
The second file has 10201 values. My first question is whether this method of saving the iterated values to file after the app stops is a good solution, given the large growth in the values as I add more lists (i.e. blendshapes)?
My second question is how I can slow down the iterations, because (in the first screenshot) the smile values start counting up from 0 to 100 and I can see them (the face moves slowly in a visible manner) but as that is happening, I notice that the second list (skeptic) apparently jumps to 100 immediately, so it is done so quickly that it cannot be recorded by the Win screen recorder...
Well i was not sure if this could be a comment or an answer so let me know if i am missing something.
For your first question if you are okay with those saved files being non-readable by humans i would suggest using BinaryReader. Because size of saved files would be smaller and when you want to read them back to make your clip it will read it faster. Also, considering you want to have 12 blend shapes and their combination this file can be huge.
For your second question iteration jumps to 100 because you only yield when the inner loop is completed. Therefore, in each frame 100 combinations of skeptical for 1 smile are added to the list. I would recommend using multi threading or Unity job system for such a task because it can be computationally costly with 12 blendshapes and all the combinations.
Let me know if i can help further. Good luck!
Related
I have this card game with the following vector3 return method:
Vector3 GetCurrentPos()
{
var _currentPos = new Vector3((_CardGameEngine._playerCardsInHand - 1) * (0 - _cardWidth / 2) + ((_cardWidth) * (_ownCardCount - 1)), -_camHeight + _cardHeight / 2, _cardLayer);
_currentPos.x = _currentPos.x * 0.8f * _CardGameEngine._playerCardsOverflowRate;
if ((_currentPos.x + (_cardWidth / 2)) > (_camWidth / 2.1))
{
_CardGameEngine._playerCardsOverflowAlert = true;
}
_currentPos.x = _currentPos.x - _edgeOffset;
return _currentPos;
}
I'm pretty new to Unity I should mention by the way. Any, this basically keeps track of where the card should go back into the player's hand depending on how many other cards are in hand etc. Actually works flawlessly except for the very last part "_edgeOffset". This is a private float that eventually gets assigned the following value:
public float _edgeOffset;
_edgeOffset = (transform.position.x + (_boxCol.bounds.size.x / 2f)) - (_camWidth / 2f);
_edgeOffset gets called in Update() in certain scenarios when a card gets enlarged but part of it will enlarge past the screen. I'm essentially trying to keep the card on-screen as it gets enlarged by finding the offset. That "_edgeOffset" should subtract that amount from the GetCurrentPos() method I mentioned earlier, but for the life of me, I can't figure out why it doesn't work!
If I print everything in the console, all of the values that calculate _edgeOffset are correct, and I can see that the value of _edgeOffset is correct as well. But if I print the value of GetCurrentPos() right after, it literally just doesn't subtract that from the x value.
I have no clue what's going on and am losing my mind haha... I was feeling pretty good about things but this is been such a sudden road-block and I have no idea what's going on. I could probably figure out a different way to achieve what I want, but I just want to understand why this isn't working.
Here's how I would do it:
In the editor, drag cards into the places that I want them, then save their Vector3 positions found in the RectTransform as an array of Vector3[]
Then, OnMouseUp, I will activate a bool in my card which runs a transform.Translate in my card update function.
This script is quickly tossed together and will need some editing but the general idea should work for your issue.
I.E.
public class Controller : MonoBehaviour
{
bool isDragging;
public Card clickedCard;
//turn into an object if you wish
public Vector3[] cardPositions;
public bool[] hasCard;
private void Update()
{
if (isDragging)
{
//run dragging code
}
//mouse released
if (Input.GetMouseButtonUp(0) && clickedCard!=null)
{
clickedCard.ReturnCard(GetFirstOpenPos());
}
}
/// <summary>
/// Requires:
/// <br/>cardPositions.Length = hasCard.Length
/// <br/>and
/// <br/>cardPositions and hasCard != null
/// </summary>
/// <returns>Vector3</returns>
Vector3 GetFirstOpenPos()
{
for(int i = 0; i < hasCard.Length; i++)
{
if (hasCard[i])
{
hasCard[i] = false;
return cardPositions[i];
}
}
throw new System.Exception("No available return positions! Fix in Editor");
}
}
public class Card : MonoBehaviour
{
bool isReturning;
float returnSpeed;
Vector3 returnPos;
private void Update()
{
if (isReturning)
{
transform.Translate(returnPos * returnSpeed);
if(Mathf.Abs(Vector3.Distance(transform.position, returnPos)) < 1)
{
transform.position = returnPos;
isReturning = false;
}
}
}
/// <summary>
/// Sets this card to be sent to returnPos
/// </summary>
/// <param name="returnPos">The global position this card will go to</param>
public void ReturnCard(Vector3 returnPos)
{
this.returnPos = returnPos;
isReturning = true;
}
}
I am developing a program to convert PDF to PPTX for specific reasons using iTextSharp.
What I've done so far is to get all text objects and image objects and locations.
But I'm feeling difficult to get Table objects without texts.
Actually it would be better if I can get them as images.
My plan is to merge all objects except text objects as a background image and put text objects at proper locations.
I tried to find similar questions here but no luck so far.
If anyone knows how to do this particular job, please answer.
Thanks.
You say
What I've done so far is to get all text objects and image objects and locations.
but you don't go into detail how you do so. I assume you use a matching IRenderListener implementation.
But IRenderListener, as you found out yourself,
only extracts images and texts.
The main missing objects are paths and their usages.
To extract them, too, you should implement IExtRenderListener which extends IRenderListener but also retrieves information about paths. To understand the callback methods, please first be aware how path related instructions work in PDFs:
First there are instructions for building the actual path; these instructions essentially
move to some position,
add a line to some position from the previous position,
add a Bézier curve to some position from the previous position using some control points, or
add an upright rectangle at some position using some width and height information.
Then there is an optional instruction to intersect the current clip path with the generated path.
Finally, there is a drawing instruction for any combination of filling the inside of the path and stroking along the path, i.e. for doing both, either one, or neither one.
This corresponds to the callbacks you retrieve in your IExtRenderListener implementation:
/**
* Called when the current path is being modified. E.g. new segment is being added,
* new subpath is being started etc.
*
* #param renderInfo Contains information about the path segment being added to the current path.
*/
void ModifyPath(PathConstructionRenderInfo renderInfo);
is called once or more often to build the actual path, PathConstructionRenderInfo containing the actual instruction type in its Operation property (compare to the PathConstructionRenderInfo constant members MOVETO, LINETO, etc. to determine the operation type) and the required coordinates / dimensions in its SegmentData property. The Ctm property additionally returns the affine transformation that currently is set to be applied to all drawing operations.
Then
/**
* Called when the current path should be set as a new clipping path.
*
* #param rule Either {#link PathPaintingRenderInfo#EVEN_ODD_RULE} or {#link PathPaintingRenderInfo#NONZERO_WINDING_RULE}
*/
void ClipPath(int rule);
is called if the current clip path shall be intersected with the constructed path.
Finally
/**
* Called when the current path should be rendered.
*
* #param renderInfo Contains information about the current path which should be rendered.
* #return The path which can be used as a new clipping path.
*/
Path RenderPath(PathPaintingRenderInfo renderInfo);
is called, PathPaintingRenderInfo containing the drawing operation in its Operation property (any combination of the PathPaintingRenderInfo constants STROKE and FILL), the rule for determining what "inside the path" means in its Rule property (NONZERO_WINDING_RULE or EVEN_ODD_RULE), and some other drawing details in the Ctm, LineWidth, LineCapStyle, LineJoinStyle, MiterLimit, and LineDashPattern properties.
try to implement IRenderListener
internal class ImageExtractor : IRenderListener
{
private int _currentPage = 1;
private int _imageCount = 0;
private readonly string _outputFilePrefix;
private readonly string _outputFolder;
private readonly bool _overwriteExistingFiles;
private ImageExtractor(string outputFilePrefix, string outputFolder, bool overwriteExistingFiles)
{
_outputFilePrefix = outputFilePrefix;
_outputFolder = outputFolder;
_overwriteExistingFiles = overwriteExistingFiles;
}
/// <summary>
/// Extract all images from a PDF file
/// </summary>
/// <param name="pdfPath">Full path and file name of PDF file</param>
/// <param name="outputFilePrefix">Basic name of exported files. If null then uses same name as PDF file.</param>
/// <param name="outputFolder">Where to save images. If null or empty then uses same folder as PDF file.</param>
/// <param name="overwriteExistingFiles">True to overwrite existing image files, false to skip past them</param>
/// <returns>Count of number of images extracted.</returns>
public static int ExtractImagesFromFile(string pdfPath, string outputFilePrefix, string outputFolder, bool overwriteExistingFiles)
{
// Handle setting of any default values
outputFilePrefix = outputFilePrefix ?? System.IO.Path.GetFileNameWithoutExtension(pdfPath);
outputFolder = String.IsNullOrEmpty(outputFolder) ? System.IO.Path.GetDirectoryName(pdfPath) : outputFolder;
var instance = new ImageExtractor(outputFilePrefix, outputFolder, overwriteExistingFiles);
using (var pdfReader = new PdfReader(pdfPath))
{
if (pdfReader.IsEncrypted())
throw new ApplicationException(pdfPath + " is encrypted.");
var pdfParser = new PdfReaderContentParser(pdfReader);
while (instance._currentPage <= pdfReader.NumberOfPages)
{
pdfParser.ProcessContent(instance._currentPage, instance);
instance._currentPage++;
}
}
return instance._imageCount;
}
#region Implementation of IRenderListener
public void BeginTextBlock() { }
public void EndTextBlock() { }
public void RenderText(TextRenderInfo renderInfo) { }
public void RenderImage(ImageRenderInfo renderInfo)
{
if (_imageCount == 0)
{
var imageObject = renderInfo.GetImage();
var imageFileName = _outputFilePrefix + _imageCount; //to get multiple file (you should add .jpg or .png ...)
var imagePath = System.IO.Path.Combine(_outputFolder, imageFileName);
if (_overwriteExistingFiles || !File.Exists(imagePath))
{
var imageRawBytes = imageObject.GetImageAsBytes();
//create a new file ()
File.WriteAllBytes(imagePath, imageRawBytes);
}
}
_imageCount++;
}
#endregion // Implementation of IRenderListener
}
I have a console application project in C# 2.0 that needs to write something to the screen in a while loop. I do not want the screen to scroll because using Console.Write or Console.Writeline method will keep displaying text on console screen incremently and thus it starts scrolling.
I want to have the string written at the same position. How can i do this?
Thanks
Use Console.SetCursorPosition to set the position. If you need to determine it first, use the Console.CursorLeft and Console.CursorTop properties.
Function to write the progress of a loop. Your loop counter can be used as the x position parameter. This prints on line 1, modify for your needs.
/// <summary>
/// Writes a string at the x position, y position = 1;
/// Tries to catch all exceptions, will not throw any exceptions.
/// </summary>
/// <param name="s">String to print usually "*" or "#"</param>
/// <param name="x">The x postion, This is modulo divided by the window.width,
/// which allows large numbers, ie feel free to call with large loop counters</param>
protected static void WriteProgress(string s, int x) {
int origRow = Console.CursorTop;
int origCol = Console.CursorLeft;
// Console.WindowWidth = 10; // this works.
int width = Console.WindowWidth;
x = x % width;
try {
Console.SetCursorPosition(x, 1);
Console.Write(s);
} catch (ArgumentOutOfRangeException e) {
} finally {
try {
Console.SetCursorPosition(origCol, origRow);
} catch (ArgumentOutOfRangeException e) {
}
}
}
I'm working on a project using C#/XNA and I'm having trouble creating water-physics in an top-down hex-based grid.
We're using an hex-tilemap with help of: http://www.redblobgames.com/grids/hexagons.
So I thought I could implement an algoritm for the water flow, but I can't seem to get it right, and it seems to be very performance-heavy.
/// <summary>
/// This method will level all the water to the same height. It does so by looking arround and make them all even
/// </summary>
/// <param name="tiles">The tile array needed to level arround</param>
public void Flow(List<Tile> tiles, Tilemap tileMap)
{
float waterAmountEachTile;
List<Water> waterTiles = new List<Water>(7);
//include self
waterTiles.Add(this);
float waterAmount = (this.waterHeight + this.ZPos);
for (int i = 0; i < tiles.Count; i++)//first loop to get all values
{
if (tiles[i].GetType() == typeof(Water))
{
waterTiles.Add((Water)tiles[i]);//check wich tiles are water and put them in a new array
waterAmount += (waterTiles[waterTiles.Count - 1].waterHeight + waterTiles[waterTiles.Count - 1].ZPos); //Increase the ammount - debuggen later werkt count goed
}
}
waterAmountEachTile = waterAmount / waterTiles.Count; //Calculate how high each tile should be ( we need this for drycheck)
dryCheck(ref waterAmount, waterTiles, waterAmountEachTile, tileMap);
waterAmountEachTile = waterAmount / waterTiles.Count; //recalculate the ammount for each tile
foreach (Water waterTile in waterTiles) //second loop to adjust the tile to the according hight
{
waterTile.waterHeight = (waterAmountEachTile - waterTile.ZPos);
}
}
/// <summary>
/// Checks if the tile should be dry or continue being a water tile.
/// </summary>
/// <param name="waterAmount"> the ammount of water to divide among the tiles</param>
/// <param name="waterTiles">The watertiles list to do the drycheck on</param>
/// <param name="waterAmountEachTile">The height to set each water tile</param>
/// <returns></returns>
private void dryCheck(ref float waterAmount, List<Water> waterTiles, float waterAmountEachTile, Tilemap tileMap)
{
//TODO dit fixen
for (int i = 0; i < waterTiles.Count; i++)
{
if (waterTiles[i].ZPos > waterAmountEachTile) //is grond hoger dan water
{
waterAmount -= waterTiles[i].ZPos;
tileMap.TileMap[waterTiles[i].XPos][waterTiles[i].YPos] = new Ground(this.graphics, waterTiles[i].XPos, waterTiles[i].YPos,
waterTiles[i].ZPos, this.size, Tilemap.HexVertices);
waterTiles.Remove(waterTiles[i]);
i--;
}
}
}
Now for my question, does any of you know of a way to implement water-physics in a top-down environment, preferably with hex-based grids.
I've looked in to several liberaries, and found Smoothed-particle hydrodynamics, but I'm not sure if it's implementable top-down, and I can't seem to find any guides in that direction.
Any help would be great, even some pointers might be enough.
Thanks in advance,
C. Venhuizen
Have you profiled your code to determine what is the slowest part?
I don't quite understand what your code is doing. Do you call Flow once for each tile, or do you call it once, and it runs over all tiles? If I were to guess, I'd say that allocating a new list each tile is going to be pretty slow. But the best way to know is to profile.
The thing that originally led me to write http://www.redblobgames.com/grids/hexagons was a top-down hex game that was all about water flow. I wrote that game in 1995, and I recently ported it to run on the web here. The algorithm I started with is simple. For each hex, traversed in random order:
calculate the water level W + elevation H
calculate the same for each neighbor
make up to half the water flow to the neighbor with the lowest W + H
The random order is so that the loop doesn't cause water to flow many hexes to the right but not many hexes to the left, or other such direction artifacts. Even cleaner would be to use a double buffer for this: write all the new values to a separate array, and then copy them back to the first array at the end (or swap the arrays).
Beyond that, there are tons of heuristics I used for erosion and other features of the (unfinished) game. The code is old and awful but if you want to take a look, you can download it here (ZIP file). See water.cpp. I avoided memory allocations in the water flow loop.
Well I'm creating an application, I'm using for loops to basically read every pixel of an image looking for patterns in pixel color (simple stuff) Anyway for some reason my application simply locks up and never reverts back to normal. I've loop through the code time and time again without seeing any real problems.
The only thing I've noticed, is the for loop in ScanPixelsLater may be exiting early. I've commented the code as much as possible,
private Point topLeftc, bottomLeftc, topRightc, bottomRightc;
/// <summary>
/// Starts the initial looping process, designed only to loop through ONCE, ScanPixelsLater takes over
/// </summary>
/// <param name="img">Image to scan</param>
public void ScanPixels(Bitmap img)
{
int whitePixel = 0;
for (int y = 100; y < img.Height; y++)
{
for (int x = 100; x < img.Width; x++)
{
if (img.GetPixel(x, y) == Color.FromArgb(255, 255, 255, 255))
{
// img.SetPixel(x, y, Color.Green);
whitePixel++;
}
else { whitePixel = 0; }
if (whitePixel == 18)
{
whitePixel = 0;
topLeftc = new Point(x - 18, y);
DetectNextWhiteLine(topLeftc, img);
}
}
}
}
/// <summary>
/// First creates the TopRight value via using the last pixel in x range, and using the current Y value
/// Then a Y loop is started 10 pixels down, within this loop is another X loop which scans the X range
/// If 10 consecutive white pixels are found, the TopLeft X value and the current Y value are used to map the
/// BottomLeft and BottomRight coordinates. Finally a new Point is created which starts (x = 1) and (y = currentYValue + 2)
/// The ScanPixelsLater method is then called, passing the new Point (newLocation).
///
/// </summary>
/// <param name="p">The x and y value of where the pixels were found</param>
/// <param name="img">Image being used</param>
private void DetectNextWhiteLine(Point p, Bitmap img)
{
int whitePixel = 0;
topRightc = new Point(img.Width, topLeftc.Y);
for (int y = p.Y + 10; y < img.Height; y++)
{
for (int x = p.X - 5; x < img.Width; x++)
{
if (img.GetPixel(x, y) == Color.FromArgb(255, 255, 255, 255))
{
whitePixel++;
}
else
{
whitePixel = 0;
}
if (whitePixel == 10)
{
bottomLeftc = new Point(topLeftc.X, y);
bottomRightc = new Point(img.Width, y);
Cords.Add(new Coordinates(topLeftc, topRightc, bottomLeftc, bottomRightc));
Point newLocation = new Point(1, y + 2);
calls++;
ScanPixelsLater(newLocation, img); //rescan the image from new y axis
}
}
}
}
/// <summary>
/// Loops through the pixels based on the p parameter, if 15 white pixels are found, the location (x & y) is
/// passed to the DetectNextWhiteLine method which fixes the next line with a similar number of white pixels.
/// </summary>
/// <param name="p">The Point(x,y) at which to start scanning</param>
/// <param name="img"></param>
private void ScanPixelsLater(Point p, Bitmap img)
{
int whitePixel = 0;
for (int y = p.Y; y < img.Height; y++)
{
for (int x = p.X; x < img.Width; x++)
{
if (img.GetPixel(x, y) == Color.FromArgb(255, 255, 255, 255))
{
whitePixel++;
}
else
{
whitePixel = 0;
}
if (whitePixel == 15)
{
bottomLeftc = new Point(topLeftc.X, y);
topLeftc = new Point(x - 15, y);
calls++;
DetectNextWhiteLine(topLeftc, img);
}
}
}
// only want this to execute after all the pixels within the entire img have been read
// possibly executing early.
DrawWhiteLines(img);
AppArgs aa = new AppArgs(true);
Change(this, aa); // custom event handler, fired behind form to update GUI
}
So, to understand why your application is hanging you need to know a little bit about how WinForm applications work.
The thread that your UI is running on also has what is called a message pump. This message pump contains messages that get passed from the operating system (and other sources) to all of the UI elements. They tell them when to change state, when to redraw themselves, etc. When you have a long running loop like yours the message pump cannot process messages. The get queued up, but never processed, and this is what it means for an application to 'hang'.
It is unlikely that your application will never recover. Your loop will eventually end and your UI will become responsive again (assuming I didn't miss an infinite loop somewhere, but I don't think that I did). However, the GDI+ GetPixel method is really very slow, and if your image is large at all that set of loops is going to take a long time to complete. You will likely have to delve into an unsafe context and obtain a pointer to the image's memory using LockBits. There are many examples of how to do that floating around here.
EDIT: After looking at your code a bit more closely it is also apparent that it is relatively inefficient. You have at least 6 levels of nested for loops going on there, so you are essentially scanning the image multiple times when only a single scan is needed.
Image processing is a resource intensive process. You need to be careful to do all of your performance intensive work as efficiently as possible.
migrated from my comments (now deleted)
It is properly not this which causes you trouble since it never comes to normal but you should never call GetPixel in a loop. It is incredible slow to use that. Instead you can use pointers or an pixel array Search for "getpixel slow" with google or stackoverflow and a large number of solutions come up.
Updated: I have looked a little bit on the code now... In the main code (ScanPixels) which is a nested for-loop, you call DetectNextWhiteLine which is also a nested for-loop and finally this calls ScanPixelsLater which is ALSO a nested for-loop. Now you potentially got a 6-level-deep nested for loop O(n^6) which calls an relatively expensive method (GetPixel). You should only iterate over the pixels a few times. This may be why it never stops because this is potential something like 1000^6*~100 instructions :)