Uno platform: load embedded resource file - embedded-resource

How do I load an embedded resource for the Android head ? My code, below works for UWP:
Assembly assembly = GetType ().GetTypeInfo ().Assembly;
string[] names = assembly.GetManifestResourceNames ();
Console.WriteLine ("Resource Names");
foreach (var name in names)
Console.WriteLine (" " + name);
using (var stream = assembly.GetManifestResourceStream (Source))
{
bmpSrc = SKBitmap.Decode (stream);
}
And, the XAML is
<controls:ExpandableImage
...
Source="UnoTest.Assets.icons.folder_tab.png"
/>
The file resides in UnoTest.Shared/Assets/ and is marked as "embedded resource".
The debug output shows that one of the "names" is
"UnoTest.Droid.Assets.icons.folder_tab.png"
indicating that my URI should be referring to the Android head.
EDIT
Ultimately, in this experiment, I am intending to paint the left part of the bitmap in the left of the target area, the right in the right, and fill the middle with an expansion of a vertical stripe from the bitmap's mid section. Then, draw some text over it.
private void OnPaintSurface(object sender, SKPaintSurfaceEventArgs e)
{
...
// identify left, right halves and a 10px wide swath of the middle of the source bitmap
SKRect rectSrcLeft = new SKRect(0, 0, bmpSrc.Width / 2, bmpSrc.Height);
SKRect rectSrcRight = new SKRect(bmpSrc.Width / 2, 0, bmpSrc.Width, bmpSrc.Height);
SKRect rectSrcMid = new SKRect(bmpSrc.Width / 2 - 5, 0, bmpSrc.Width / 2 + 5, bmpSrc.Height);
// create a new bitmap containing a 10 pixel wide swatch from middle of bmpSrc
SKBitmap bmpSrcMid = new SKBitmap(10, bmpSrc.Height);
using (SKCanvas tempCanvas = new SKCanvas(bmpSrcMid))
{
SKRect rectDest = new SKRect(0, 0, rectSrcMid.Width, rectSrcRight.Height);
tempCanvas.DrawBitmap(bmpSrc, rectSrcMid, rectDest);
}
var canvas = e.Surface.Canvas;
using (SKPaint paint = new SKPaint())
{
canvas.Save();
float hDest = canvas.DeviceClipBounds.Height;
float scale = hDest / (float)bmpSrc.Height;
canvas.Scale(scale);
paint.IsAntialias = true;
// determine dest rect for middle section
float rightDest = (float)textBounds.Width / scale; // rightmost point of whole target area
SKRect rectDestMid = new SKRect(rectSrcLeft.Width, 0, rightDest - rectSrcRight.Width, rectSrcRight.Height);
// left part of tab
canvas.DrawBitmap(bmpSrc, rectSrcLeft, rectSrcLeft, paint);
// right part of tab
{
SKRect rectDest = new SKRect(rectDestMid.Right, 0, rightDest, rectSrcRight.Height);
canvas.DrawBitmap(bmpSrc, rectSrcRight, rectDest, paint);
}
// mid part of tab
paint.Shader = SKShader.CreateBitmap(bmpSrcMid,
SKShaderTileMode.Repeat,
SKShaderTileMode.Repeat);
canvas.DrawRect(rectDestMid, paint);
canvas.Restore(); // back to orig scale
}
using (SKPaint paint = new SKPaint { Color = SKColors.Black })
{
float leftText = 20; // matches padding in ListPage.xaml
float bottomText = canvas.DeviceClipBounds.Height / 2 + textCoreHeight / 2;
canvas.DrawText(Label, new SKPoint(leftText, bottomText), paint);
}
}
The XAML for the control is:
<UserControl
x:Class="UnoTest.Shared.Controls.ExpandableImage"
...
<skia:SKXamlCanvas x:Name="EICanvas" PaintSurface="OnPaintSurface" />
</UserControl>
Do I need to write some code to modify the "generic" URI for the Android case ?

Embedded Resources defined in shared projects are sharing the default namespace of the project referencing that shared project, making the file name different in all projects by default in Uno templates.
You have multiple options:
Change the default namespace to be the same in all projects.
Skip the first two dots and use this as the base
Use a different project (a .NET Standard 2.0 project will do) and place your resources there

Here's what I ended up doing. It is not 100% robust but pretty close. And it's really simple.
if (Source == null)
return;
string sourceWithNameSpace = null;
Assembly assembly = GetType ().GetTypeInfo ().Assembly;
string[] names = assembly.GetManifestResourceNames ();
foreach (var name in names)
{
if (name.EndsWith (Source))
{
sourceWithNameSpace = name;
break;
}
}
if (sourceWithNameSpace == null)
return;
using (var stream = assembly.GetManifestResourceStream (sourceWithNameSpace))
{
bmpSrc = SKBitmap.Decode (stream);
}
And, in the XML file, leave off the project head from the Source path, e.g.:
Source="Assets.icons.folder_tab.png"

Related

AR camera distance measurement

I have a question about AR(Augmented Reality).
I want to know how to show the distance information(like centermeter...) between AR camera and target object. (Using Smartphone)
Can I do that in Unity ? Should I use AR Foundation? and with ARcore? How to write code?
I tried finding some relative code(below), but it seems just like Printing information between object and object, nothing about "AR camera"...
var other : Transform;
if (other) {
var dist = Vector3.Distance(other.position, transform.position);
print ("Distance to other: " + dist);
}
Thank again!
Here is how to do it Unity and AR Foundation 4.1.
This example script prints the depth in meters at the depth texture's center and works both with ARCore and ARKit:
using System;
using System.Collections;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.XR.ARFoundation;
using UnityEngine.XR.ARSubsystems;
public class GetDepthOfCenterPixel : MonoBehaviour {
// assign this field in inspector
[SerializeField] AROcclusionManager manager = null;
IEnumerator Start() {
while (ARSession.state < ARSessionState.SessionInitializing) {
// manager.descriptor.supportsEnvironmentDepthImage will return a correct value if ARSession.state >= ARSessionState.SessionInitializing
yield return null;
}
if (!manager.descriptor.supportsEnvironmentDepthImage) {
Debug.LogError("!manager.descriptor.supportsEnvironmentDepthImage");
yield break;
}
while (true) {
if (manager.TryAcquireEnvironmentDepthCpuImage(out var cpuImage) && cpuImage.valid) {
using (cpuImage) {
Assert.IsTrue(cpuImage.planeCount == 1);
var plane = cpuImage.GetPlane(0);
var dataLength = plane.data.Length;
var pixelStride = plane.pixelStride;
var rowStride = plane.rowStride;
Assert.AreEqual(0, dataLength % rowStride, "dataLength should be divisible by rowStride without a remainder");
Assert.AreEqual(0, rowStride % pixelStride, "rowStride should be divisible by pixelStride without a remainder");
var numOfRows = dataLength / rowStride;
var centerRowIndex = numOfRows / 2;
var centerPixelIndex = rowStride / (pixelStride * 2);
var centerPixelData = plane.data.GetSubArray(centerRowIndex * rowStride + centerPixelIndex * pixelStride, pixelStride);
var depthInMeters = convertPixelDataToDistanceInMeters(centerPixelData.ToArray(), cpuImage.format);
print($"depth texture size: ({cpuImage.width},{cpuImage.height}), pixelStride: {pixelStride}, rowStride: {rowStride}, pixel pos: ({centerPixelIndex}, {centerRowIndex}), depthInMeters of the center pixel: {depthInMeters}");
}
}
yield return null;
}
}
float convertPixelDataToDistanceInMeters(byte[] data, XRCpuImage.Format format) {
switch (format) {
case XRCpuImage.Format.DepthUint16:
return BitConverter.ToUInt16(data, 0) / 1000f;
case XRCpuImage.Format.DepthFloat32:
return BitConverter.ToSingle(data, 0);
default:
throw new Exception($"Format not supported: {format}");
}
}
}
I'm working on AR depth image as well and the basic idea is:
Acquire an image using API, normally it's in format Depth16;
Split the image into shortbuffers, as Depth16 means each pixel is 16 bits;
Get the distance value, which is stored in the lower 13 bits of each shortbuffer, you can do this by doing (shortbuffer & 0x1ff), then you can have the distance for each pixel, normally it's in millimeters.
By doing this through all the pixels, you can create a depth image and store it as jpg or other formats, here's the sample code of using AR Engine to get the distance:
try (Image depthImage = arFrame.acquireDepthImage()) {
int imwidth = depthImage.getWidth();
int imheight = depthImage.getHeight();
Image.Plane plane = depthImage.getPlanes()[0];
ShortBuffer shortDepthBuffer = plane.getBuffer().asShortBuffer();
File sdCardFile = Environment.getExternalStorageDirectory();
Log.i(TAG, "The storage path is " + sdCardFile);
File file = new File(sdCardFile, "RawdepthImage.jpg");
Bitmap disBitmap = Bitmap.createBitmap(imwidth, imheight, Bitmap.Config.RGB_565);
for (int i = 0; i < imheight; i++) {
for (int j = 0; j < imwidth; j++) {
int index = (i * imwidth + j) ;
shortDepthBuffer.position(index);
short depthSample = shortDepthBuffer.get();
short depthRange = (short) (depthSample & 0x1FFF);
//If you only want the distance value, here it is
byte value = (byte) depthRange;
byte value = (byte) depthRange ;
disBitmap.setPixel(j, i, Color.rgb(value, value, value));
}
}
//I rotate the image for a better view
Matrix matrix = new Matrix();
matrix.setRotate(90);
Bitmap rotatedBitmap = Bitmap.createBitmap(disBitmap, 0, 0, imwidth, imheight, matrix, true);
try {
FileOutputStream out = new FileOutputStream(file);
rotatedBitmap.compress(Bitmap.CompressFormat.JPEG, 90, out);
out.flush();
out.close();
MainActivity.num++;
} catch (Exception e) {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
}
While the answers are great, they may be too complicated and advanced for this question, which is about the distance between the ARCamera and another object, and not about the depth of pixels and their occlusion.
transform.position gives you the position of whatever game object you attach the script to in the hierarchy. So attach the script to the ARCamera object. And obviously, other should be the target object.
Alternately, you can get references to the two game objects using inspector variables or GetComponent
/raycasting should be in update/
Ray ray = new Ray(cam.transform.position, cam.transform.forward);
if (Physics.Raycast(ray, out info, 50f, layerMaskAR))//50 meters detection range bcs of 50f
{
distanca.text = string.Format("{0}: {1:N2}m", info.collider.name, info.distance, 2);
}
This is func that does it what u need with this is ofc on UI txt element and layer assigne to object/prefab.
int layerMaskAR = 1 << 6; (here u see 6 bcs 6th is my custom layer ,,layerMaskAR,,)
This is ray cating on to objects in only this layer rest object are ignored(if u dont want to ignore anything remove layerMask from raycast and it will print out name of anything with collider).
Totally doable by this line of code
Vector3.Distance(gameObject.transform.position, Camera.main.transform.position)

Dumping a customized LINQPad chart inline

If I don't customize a chart in LINQPad there is a method for dumping inline using linqpadChart.DumpInline().
Is there an similar method for dumping a customized chart inline, or is creating and dumping a Bitmap like I've done in this example the only way?
string[] xSeries = { "John", "Mary", "Sue" };
int[] ySeries = { 100, 120, 140 };
var winChart = xSeries.Chart().AddYSeries (ySeries, Util.SeriesType.Pie).ToWindowsChart();
// Make tweaks/customizations:
var area = winChart.ChartAreas.First();
area.Area3DStyle.Enable3D = true;
area.Area3DStyle.Inclination = 50;
winChart.Series.First().Points[2].SetCustomProperty ("Exploded", "true");
// Draw it to a bitmap and then dump it:
using (var bitmap = new Bitmap(500, 500))
{
winChart.DrawToBitmap(bitmap, new Rectangle(Point.Empty, bitmap.Size));
bitmap.Dump();
}
Yes, this is similar to what DumpInline does behind the scenes, albeit with a WebChart.
Here's the code from LINQPadChart, if you'd like to copy it to My Extensions
public static class MyExtensions
{
public static Bitmap ToBitmap (this Chart chart, int width = 0, int height = 0)
{
if (width <= 0) width = Screen.PrimaryScreen.WorkingArea.Width / 2;
if (height <= 0) height = width / 2;
var img = new Bitmap (width, height);
using (Graphics g = Graphics.FromImage (img))
chart.Printing.PrintPaint (g, new Rectangle (0, 0, width, height));
return img;
}
}
This method requires the following namespaces:
System.Drawing
System.Windows.Forms
System.Windows.Forms.DataVisualization.Charting
Edit: changed the method so that it uses the Windows Forms chart rather than the Web Forms chart, so that it also works in .NET Core.

iTextSharp IExtRenderListener and boundingbox [duplicate]

I have a pdf which comprises of some data, followed by some whitespace. I don't know how large the data is, but I'd like to trim off the whitespace following the data
PdfReader reader = new PdfReader(PDFLOCATION);
Rectangle rect = new Rectangle(700, 2000);
Document document = new Document(rect);
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(SAVELCATION));
document.open();
int n = reader.getNumberOfPages();
PdfImportedPage page;
for (int i = 1; i <= n; i++) {
document.newPage();
page = writer.getImportedPage(reader, i);
Image instance = Image.getInstance(page);
document.add(instance);
}
document.close();
Is there a way to clip/trim the whitespace for each page in the new document?
This PDF contains vector graphics.
I'm usung iTextPDF, but can switch to any Java library (mavenized, Apache license preferred)
As no actual solution has been posted, here some pointers from the accompanying itext-questions mailing list thread:
As you want to merely trim pages, this is not a case of PdfWriter + getImportedPage usage but instead of PdfStamper usage. Your main code using a PdfStamper might look like this:
PdfReader reader = new PdfReader(resourceStream);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream("target/test-outputs/test-trimmed-stamper.pdf"));
// Go through all pages
int n = reader.getNumberOfPages();
for (int i = 1; i <= n; i++)
{
Rectangle pageSize = reader.getPageSize(i);
Rectangle rect = getOutputPageSize(pageSize, reader, i);
PdfDictionary page = reader.getPageN(i);
page.put(PdfName.CROPBOX, new PdfArray(new float[]{rect.getLeft(), rect.getBottom(), rect.getRight(), rect.getTop()}));
stamper.markUsed(page);
}
stamper.close();
As you see I also added another argument to your getOutputPageSize method to-be. It is the page number. The amount of white space to trim might differ on different pages after all.
If the source document did not contain vector graphics, you could simply use the iText parser package classes. There even already is a TextMarginFinder based on them. In this case the getOutputPageSize method (with the additional page parameter) could look like this:
private Rectangle getOutputPageSize(Rectangle pageSize, PdfReader reader, int page) throws IOException
{
PdfReaderContentParser parser = new PdfReaderContentParser(reader);
TextMarginFinder finder = parser.processContent(page, new TextMarginFinder());
Rectangle result = new Rectangle(finder.getLlx(), finder.getLly(), finder.getUrx(), finder.getUry());
System.out.printf("Text/bitmap boundary: %f,%f to %f, %f\n", finder.getLlx(), finder.getLly(), finder.getUrx(), finder.getUry());
return result;
}
Using this method with your file test.pdf results in:
As you see the code trims according to text (and bitmap image) content on the page.
To find the bounding box respecting vector graphics, too, you essentially have to do the same but you have to extend the parser framework used here to inform its listeners (the TextMarginFinder essentially is a listener to drawing events sent from the parser framework) about vector graphics operations, too. This is non-trivial, especially if you don't know PDF syntax by heart yet.
If your PDFs to trim are not too generic but can be forced to include some text or bitmap graphics in relevant positions, though, you could use the sample code above (probably with minor changes) anyways.
E.g. if your PDFs always start with text on top and end with text at the bottom, you could change getOutputPageSize to create the result rectangle like this:
Rectangle result = new Rectangle(pageSize.getLeft(), finder.getLly(), pageSize.getRight(), finder.getUry());
This only trims top and bottom empty space:
Depending on your input data pool and requirements this might suffice.
Or you can use some other heuristics depending on your knowledge on the input data. If you know something about the positioning of text (e.g. the heading to always be centered and some other text to always start at the left), you can easily extend the TextMarginFinder to take advantage of this knowledge.
Recent (April 2015, iText 5.5.6-SNAPSHOT) improvements
The current development version, 5.5.6-SNAPSHOT, extends the parser package to also include vector graphics parsing. This allows for an extension of iText's original TextMarginFinder class implementing the new ExtRenderListener methods like this:
#Override
public void modifyPath(PathConstructionRenderInfo renderInfo)
{
List<Vector> points = new ArrayList<Vector>();
if (renderInfo.getOperation() == PathConstructionRenderInfo.RECT)
{
float x = renderInfo.getSegmentData().get(0);
float y = renderInfo.getSegmentData().get(1);
float w = renderInfo.getSegmentData().get(2);
float h = renderInfo.getSegmentData().get(3);
points.add(new Vector(x, y, 1));
points.add(new Vector(x+w, y, 1));
points.add(new Vector(x, y+h, 1));
points.add(new Vector(x+w, y+h, 1));
}
else if (renderInfo.getSegmentData() != null)
{
for (int i = 0; i < renderInfo.getSegmentData().size()-1; i+=2)
{
points.add(new Vector(renderInfo.getSegmentData().get(i), renderInfo.getSegmentData().get(i+1), 1));
}
}
for (Vector point: points)
{
point = point.cross(renderInfo.getCtm());
Rectangle2D.Float pointRectangle = new Rectangle2D.Float(point.get(Vector.I1), point.get(Vector.I2), 0, 0);
if (currentPathRectangle == null)
currentPathRectangle = pointRectangle;
else
currentPathRectangle.add(pointRectangle);
}
}
#Override
public Path renderPath(PathPaintingRenderInfo renderInfo)
{
if (renderInfo.getOperation() != PathPaintingRenderInfo.NO_OP)
{
if (textRectangle == null)
textRectangle = currentPathRectangle;
else
textRectangle.add(currentPathRectangle);
}
currentPathRectangle = null;
return null;
}
#Override
public void clipPath(int rule)
{
}
(Full source: MarginFinder.java)
Using this class to trim the white space results in
which is pretty much what one would hope for.
Beware: The implementation above is far from optimal. It is not even correct as it includes all curve control points which is too much. Furthermore it ignores stuff like line width or wedge types. It actually merely is a proof-of-concept.
All test code is in TestTrimPdfPage.java.

How to translate x, y origin of a PDPage?

I'm new to Apache's PDFBox. I'm using version 2.0.0, taken from the SVN repository.
Actually I try to increase the cropbox of a PDPage object. OK, no challenge. But the page content remains in the lower left corner of the cropbox. I want it centered in the new cropbox space.
I understood that all content is positioned absolutely in PDF. So my question: Is there a way using PDFBox to translate the origin (x, y) of my PDPage or the content elements?
Regards
Hans
The first approach would be to simply change the crop box like this:
PDDocument document = PDDocument.load(new File("data/test.pdf"));
PDDocumentCatalog catalog = document.getDocumentCatalog();
#SuppressWarnings("unchecked")
List<PDPage> pages = catalog.getAllPages();
float expand = 72;
for (PDPage page : pages)
{
PDRectangle cropBox = page.findCropBox();
PDRectangle newCropBox = new PDRectangle();
newCropBox.setLowerLeftX(cropBox.getLowerLeftX() - expand);
newCropBox.setLowerLeftY(cropBox.getLowerLeftY() - expand);
newCropBox.setUpperRightX(cropBox.getUpperRightX() + expand);
newCropBox.setUpperRightY(cropBox.getUpperRightY() + expand);
page.setCropBox(newCropBox);
}
document.save("data/out/test-expand-crop-simple.pdf");
This only works sometimes, though, because according to the specification ISO 32000-1, section 14.11.2 Page Boundaries
The crop, bleed, trim, and art boxes shall not ordinarily extend beyond the boundaries of the media box. If they do, they are effectively reduced to their intersection with the media box.
(also see this answer)
Thus, we have to make sure that the crop box even after enlarging still fits into the media box, e.g. like this:
PDDocument document = PDDocument.load(new File("data/test.pdf"));
PDDocumentCatalog catalog = document.getDocumentCatalog();
#SuppressWarnings("unchecked")
List<PDPage> pages = catalog.getAllPages();
float expand = 72;
for (PDPage page : pages)
{
PDRectangle cropBox = page.findCropBox();
PDRectangle newCropBox = new PDRectangle();
newCropBox.setLowerLeftX(cropBox.getLowerLeftX() - expand);
newCropBox.setLowerLeftY(cropBox.getLowerLeftY() - expand);
newCropBox.setUpperRightX(cropBox.getUpperRightX() + expand);
newCropBox.setUpperRightY(cropBox.getUpperRightY() + expand);
page.setCropBox(newCropBox);
PDRectangle mediaBox = page.findMediaBox();
PDRectangle newMediaBox = new PDRectangle();
newMediaBox.setLowerLeftX(mediaBox.getLowerLeftX() - expand);
newMediaBox.setLowerLeftY(mediaBox.getLowerLeftY() - expand);
newMediaBox.setUpperRightX(mediaBox.getUpperRightX() + expand);
newMediaBox.setUpperRightY(mediaBox.getUpperRightY() + expand);
page.setMediaBox(newMediaBox);
}
document.save("data/out/test-expand-crop-and-media.pdf");
The central code to manipulate the crop box looks as follows:
#Override
protected void treeNodeChanged(PDFTreeNode node)
{
if (node instanceof PDFFloatNode)
{
PDFFloatNode nodeF = (PDFFloatNode)node;
String strDataKey = node.getDataKey();
if ("x".equals(strDataKey))
{
m_rect.setLowerLeftX(nodeF.getFloat());
}
else if ("y".equals(strDataKey))
{
m_rect.setLowerLeftY(nodeF.getFloat());
}
else if ("width".equals(strDataKey))
{
m_rect.setUpperRightX(nodeF.getFloat());
}
else if ("height".equals(strDataKey))
{
m_rect.setUpperRightY(nodeF.getFloat());
}
}
if (m_parent != null)
{
m_parent.treeNodeChanged(node);
}
}
Where m_rect is an instance of PDRectangle. In fact this information doesn't help you, #mkl. In between I found an information to solve the problem (I hope). There is an operation which is obviously not known in PDFBox.
<</PageOffset [-20 20]>> setpagedevice
Now I'm looking for a way to implement it into the PDF.
Thanks
Hans
The Pageoffset dictionary entry was not very helpful. It can be used as a parameter for GhostScript. But GS does not really create a dictionary entry. Instead it offsets the content of all pages. A capability which doesn't exist in PDFBox.
I found a solution using LayerUtility:
PDDocument docIn = null;
try
{
docIn = PDDocument.load("./pdf/Test1.pdf");
float fBorder = 10 * MM_TO_UNITS; // Arbitrary 10 mm
PDDocument docOut = new PDDocument();
PDPage pageIn = (PDPage)docIn.getDocumentCatalog().getPages().getKids().get(0);
PDRectangle rectCrop = pageIn.findCropBox();
PDPage pageClone = clonePage(docOut, pageIn, true),
pageOut = new PDPage(
new PDRectangle(rectCrop.getWidth() + 2 * fBorder, rectCrop.getHeight() + 2 * fBorder)
);
docOut.addPage(pageOut);
PDPageContentStream stream = new PDPageContentStream(docOut, pageOut);
stream.close();
LayerUtility lu = new LayerUtility(docOut);
lu.wrapInSaveRestore(pageOut);
PDXObjectForm xobj = lu.importPageAsForm(docIn, pageClone);
AffineTransform at = new AffineTransform();
// That's the point where x,y offset takes place
at.setToTranslation(fBorder, fBorder);
lu.appendFormAsLayer(pageOut, xobj, at, "layerx");
docOut.addPage(pageOut);
docOut.save("./pdf/Test1out.pdf");
}
finally
{
if (docIn != null)
{
docIn.close();
}
}
I'm not very happy with this. Because it changes the page structure. But at least I have a solution.
Regards
Hans
To increase the CropBox, you set the MediaBox like this:
PDRectangle box = new PDRectangle(pageWidth, pageHeight);
page.setMediaBox(box); // MediaBox > BleedBox > TrimBox/CropBox

Unity3D: Reduce number of bones at runtime

I am creating something like in the sims where you have a character and you can add different clothes to the character.
All the models I use are using the same bones and rig. I have put these models in different asset bundles. At runtime I read the asset bundles and combine these models in one SkinnedMeshRenderer and thus in one object. But every time I add another object the bone count goes up.
I know why this is happening, but I would like to reduce the number of bones again.
I tried to find the duplicate bones and delete those and the bindpose on the same index as the bone deleted, but this still gives me the error "Number of bindposes doesn't match number of bones" even though they are both 45.
Here is the code that attaches the models:
private void UpdateSkinnedMesh()
{
float startTime = Time.realtimeSinceStartup;
// Create a list of for all data
List combineInstances = new List();
List materials = new List();
List bones = new List();
Transform[] transforms = baseModel.GetComponentsInChildren();
//TEMP
List elements = new List();
elements.Add(body);
if(_currentActiveProps.ContainsKey(ItemSubCategory.Skin))
elements.Add(tutu);
//Go over all active elements
foreach (CharacterElement element in elements)
{
//Get the skinned mesh renderer
SkinnedMeshRenderer smr = element.GetSkinnedMeshRenderer();
//Add materials to the entire list
materials.AddRange(smr.materials);
//Add all submeshes to a combined mesh
for (int sub = 0; sub < smr.sharedMesh.subMeshCount; sub++)
{
CombineInstance ci = new CombineInstance();
ci.mesh = smr.sharedMesh;
ci.subMeshIndex = sub;
combineInstances.Add(ci);
}
//Bones are not saved in asset bundle, get the names and reference them again
foreach (string bone in element.GetBoneNames())
{
foreach (Transform transform in transforms)
{
if (transform.name != bone) continue;
bones.Add(transform);
break;
}
}
//Destroy the temp object
Destroy(smr.gameObject);
}
//Get skinned mesh renderer
SkinnedMeshRenderer r = baseModel.GetComponentInChildren();
//Create enew combined mesh
r.sharedMesh = new Mesh();
r.sharedMesh.CombineMeshes(combineInstances.ToArray(), false, false);
//Add bones and materials
r.bones = bones.ToArray();
r.materials = materials.ToArray();
Debug.Log("Bindposes: " + r.sharedMesh.bindposes.Length);
Debug.Log("Generating character took: " + (Time.realtimeSinceStartup - startTime) * 1000 + " ms");
Debug.Log("Bone count: " + r.bones.Length);
}
I already have asked the question on Unity Answers, but because it takes a few hours before a moderator approves the question, I wanted to try here as well.